Ruft die Zeilennummer vom Byte-Offset ab

12

Byte-Offset für eine Datei.

Gibt es ein Tool, das die Zeilennummer für dieses Byte angibt?

  • Byteanzahl beginnend mit Null, wie in: Das erste Byte ist 0, nicht 1.
  • Zeilennummer beginnend mit 1.
  • Die Datei kann sowohl einfachen Text als auch "binäre" Blobs, Multibyte-Zeichen usw. enthalten. Der Abschnitt, an dem ich interessiert bin: Dateiende, enthält jedoch nur ASCII.

Beispiel, Datei:

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

Ein Byte-Offset 8, der mir eine Zeile geben würde 3.

Ich schätze, ich könnte so etwas verwenden, um die Zeilennummer zu finden:

 ein. tail -c+(offset + 1) file | wc -l, Hier +1als tailZählungen von 1.
 b. wc -l file
 c. tail -n+num Wo numist dann ?a - b + 1

Aber ... gibt es ein ziemlich verbreitetes Werkzeug, das mir numdirekt zur Verfügung stehen kann?


Bearbeiten, äh: oder das offensichtlichere:

head -c+offset file | wc -l
user367890
quelle
2
Binärdateien haben keine Zeilen.
Kusalananda
@Kusalananda: Zeilen sind in diesem Zusammenhang Daten, die durch 0x0aBytes getrennt sind .
user367890
3
Wahrscheinlich nicht das, was Sie fragen, aber Vim hat eine Funktion dafür. Es zählt Offsets von 1, also : :echo byte2line(offset+1).
Satō Katsura
@SatoKatsura: Ja und danke. Versuchte es zuerst mit vim. Aber selbst mit vim -bund vim+ set binary+ geöffneter Datei wurde es beschädigt. (Ah. Plötzlich erinnere ich mich, welches Plugin es vermasselt). Aber trotzdem, da ich dies in Stapeln und in Kombination mit einer Reihe von Skripten verwende, wurde Vim früh aufgegeben. Aber trotzdem +1.
user367890
@ user367890 Eine Binärdatei kann 0xaüberall sein. Das Konzept der Zeilen in einer Binärdatei ist bedeutungslos.
user207421

Antworten:

14

In Ihrem Beispiel

001
002
003
004

Byte Nummer 8 ist die zweite neue Zeile, nicht die 0in der nächsten Zeile.

Im Folgenden erhalten Sie die Anzahl der vollständigen Zeilen nach $bBytes:

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

Es wird 2mit bSatz auf 8 und 1mit bSatz auf 7 berichtet.

Das ddDienstprogramm, wie es hier verwendet wird, liest aus der Datei data.inund liest $bBlöcke mit einer Größe von 1 Byte.

Wie "icarus" in den Kommentaren unten zu Recht hervorhebt, ist die Verwendung bs=1ineffizient. In diesem speziellen Fall ist es effizienter zu tauschen bsund count:

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

Dies hat den gleichen Effekt wie der erste ddBefehl, liest jedoch nur einen $bByteblock.

Das wcDienstprogramm zählt Zeilenumbrüche und eine "Zeile" in Unix wird immer durch einen Zeilenumbruch abgeschlossen. Der obige Befehl sagt also immer noch aus, 2ob Sie einen bWert unter 12 eingestellt haben (die folgende neue Zeile). Das gesuchte Ergebnis ist daher die Anzahl der oben genannten Pipeline-Berichte plus 1.

Dies zählt natürlich auch die zufälligen Zeilenumbrüche im binären Blob-Teil Ihrer Datei, der dem ASCII-Text vorausgeht. Wenn Sie wissen, wo das ASCII-Bit beginnt, können Sie skip="$offset"dem ddBefehl hinzufügen , wo $offsetdie Anzahl der Bytes ist, die in die Datei gesprungen werden sollen.

Kusalananda
quelle
@don_crisstihead: unknown option -- c
Kusalananda
@Kusalananda Sie verwenden BSD-Kopf, Optionen gibt es anders
Sergiy Kolodyazhnyy
@Serg :-) Das weiß ich genau. Wir wissen nicht, was das OP verwendet, also bleibe ich bei POSIX.
Kusalananda
1
Wie ich in Q erwähnt habe: Die Anzahl der Bytes beginnt mit 0, nicht mit 1, daher 8 == 0 ...
user367890
@ user367890 In diesem Fall verwenden $(( b - 1 )).
Kusalananda
4

Derzeit gibt es kein solches dediziertes Tool, obwohl es in Python ziemlich einfach ausgeführt werden kann:

#!/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)

Die Verwendung ist einfach:

line4byte.py <FILE> <BYTE>

Testlauf:

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

Dies ist ein sehr schnelles und einfaches Skript. Es wird nicht überprüft, ob die Datei leer ist oder nicht, daher funktioniert es nur bei nicht leeren Dateien.

Sergiy Kolodyazhnyy
quelle
4

Verfolgen Sie die angezeigten Bytes und geben Sie die aktuelle Zeilennummer aus, falls der angegebene Versatz innerhalb der Summe liegt:

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

Oder ausführlich:

#!/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;
Thrig
quelle