Ottieni il numero di riga dall'offset di byte


12

Avere offset di byte per un file.

Esiste uno strumento che fornisce il numero di riga per questo byte?

  • Conteggio byte che inizia con zero, come in: il primo byte è 0 non 1.
  • Numero di riga che inizia con 1.
  • Il file può avere sia testo semplice, BLOB "binari", caratteri multibyte ecc. Ma la sezione che mi interessa: Fine del file, ha solo ASCII.

Esempio, file:

001
002
003  <<-- first zero on this line is byte 8
004

Avere offset di byte 8che mi darebbe la linea 3.

Immagino che potrei usare qualcosa del genere per trovare il numero di riga:

 un. tail -c+(offset + 1) file | wc -l, qui +1come tailconta da 1.
 b. wc -l file
 c. Allora tail -n+num dove numèa - b + 1

Ma ... esiste uno strumento abbastanza comune che può darmi numdirettamente?


Modifica, err: o il più ovvio:

head -c+offset file | wc -l

2
I file binari non hanno linee.
Kusalananda

@Kusalananda: le linee in questo contesto sono dati separati da 0x0abyte.
user367890

3
Probabilmente non è quello che stai chiedendo, ma Vim ha una funzione per questo. Conta offset da 1, in modo da: :echo byte2line(offset+1).
Satō Katsura,

@SatoKatsura: Sì, e grazie. Ho provato prima con vim. Ma anche con vim -be vim+ set binary+ il file aperto è stato danneggiato. (Ah. Improvvisamente ricordo quale plugin lo rovina). Ma, comunque, mentre lo uso in batch e in combinazione con una serie di script Vim è stato presto abbandonato. Ma +1 comunque.
user367890

@ user367890 Un file binario può avere 0xaovunque. Il concetto di linee in un file binario non ha senso.
user207421

Risposte:


14

Nel tuo esempio,

001
002
003
004

il byte numero 8 è la seconda riga nuova, non la 0riga successiva.

Quanto segue ti darà il numero di righe complete dopo i $bbyte:

$ dd if=data.in bs=1 count="$b" | wc -l

Riferirà 2con bset a 8 e riporterà 1con bset a 7.

L' ddutilità, come viene usata qui, leggerà dal file data.ine leggerà $bblocchi di dimensione 1 byte.

Come giustamente sottolinea "Icaro" nei commenti qui sotto, l'utilizzo bs=1è inefficiente. È più efficiente, in questo caso particolare, scambiare bse count:

$ dd if=data.in bs="$b" count=1 | wc -l

Ciò avrà lo stesso effetto del primo ddcomando, ma leggerà solo un blocco di $bbyte.

L' wcutilità conta le nuove linee e una "linea" in Unix è sempre terminata da una nuova linea. Quindi il comando precedente dirà comunque 2se si imposta bsu un valore inferiore a 12 (la nuova riga seguente). Il risultato che stai cercando è quindi qualunque sia il numero dei rapporti della pipeline sopra, più 1.

Ciò conterà ovviamente anche le newline casuali nella parte BLOB binaria del file che precede il testo ASCII. Se sapessi dove inizia il bit ASCII, potresti aggiungere skip="$offset"al ddcomando, dove $offsetè il numero di byte da saltare nel file.


@don_crisstihead: unknown option -- c
Kusalananda

@Kusalananda Stai usando la testa di BSD, le opzioni sono diverse
Sergiy Kolodyazhnyy

@Serg :-) Ne sono ben consapevole. Non sappiamo cosa utilizzi l'OP, quindi mi attengo a POSIX.
Kusalananda

1
Come ho detto in Q: il conteggio dei byte inizia con 0, non 1, quindi 8 == 0 ...
user367890

@ user367890 In tal caso, utilizzare $(( b - 1 )).
Kusalananda

4

Attualmente non esiste uno strumento dedicato come quello, sebbene possa essere fatto abbastanza facilmente in Python:

#!/usr/bin/env python3
import sys
import os

offset = int(sys.argv[2])
newline = 1
with open(sys.argv[1]) as fd:
    fd.seek(offset)
    while True:
        try:
            byte = fd.read(1)
            if byte == '\n': newline+=1
            #print(byte)
            offset = offset - 1
            fd.seek(offset)
        except ValueError:
            break
print(newline)

L'uso è semplice:

line4byte.py <FILE> <BYTE>

Prova:

$ cat input.txt
001
002
003
004
$ chmod +x ./line4byte.py                                                     
$ ./line4byte.py input.txt 8                                                  
3

Questo è uno script molto veloce e semplice. Non controlla se il file è vuoto o meno, quindi funziona solo su file non vuoti.


4

Tieni traccia dei byte visualizzati ed emette il numero di riga corrente nel caso in cui l'offset dato sia compreso nella somma:

perl -E '$off=shift;while(<>){$sum+=length;if($sum>=$off){say $.;exit}}' 8 file

O alla fine:

#!/usr/bin/env perl
use strict;
use warnings;
die "Usage: $0 offset file|-\n" if @ARGV != 2;
my $offset = shift;
shift if $ARGV[0] eq '-';
my $sum;
while (readline) {
    $sum += length;
    if ($sum >= $offset) {
        print "$.\n";
        exit;
    }
}
exit 1;

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.