Obtener el número de línea del desplazamiento de bytes

12

Tener desplazamiento de bytes para un archivo.

¿Existe alguna herramienta que proporcione el número de línea para este byte?

  • Recuento de bytes que comienza con cero, como en: primer byte es 0 no 1.
  • Número de línea que comienza con 1.
  • El archivo puede tener texto sin formato, blobs "binarios", caracteres multibyte, etc. Pero la sección que me interesa: Fin del archivo, solo tiene ASCII.

Ejemplo, archivo:

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

Tener un byte offset 8me daría una línea 3.

Supongo que podría usar algo como esto para encontrar el número de línea:

 a. tail -c+(offset + 1) file | wc -l, aquí +1como tailcuenta desde 1.
 b. wc -l file
 C. Entonces tail -n+num donde numestaa - b + 1

Pero ... ¿hay una herramienta bastante común que me pueda dar numdirectamente?


Editar, err: o lo más obvio:

head -c+offset file | wc -l
usuario367890
fuente
2
Los archivos binarios no tienen líneas.
Kusalananda
@Kusalananda: las líneas en este contexto son datos separados por 0x0abytes.
user367890
3
Probablemente no sea lo que estás preguntando, pero Vim tiene una función para ello. Cuenta compensaciones de 1, por lo que: :echo byte2line(offset+1).
Satō Katsura el
@SatoKatsura: Sí, y gracias. Intenté con vim primero. Pero incluso con vim -by vim+ set binary+ archivo abierto se corrompió. (Ah. De repente recuerdo qué plugin lo estropea). Pero, de todos modos, cuando uso esto en lotes y en combinación con una variedad de guiones, Vim fue abandonado temprano. Pero de todos modos +1.
user367890
@ user367890 Un archivo binario puede tener en 0xacualquier lugar. El concepto de líneas en un archivo binario no tiene sentido.
user207421

Respuestas:

14

En tu ejemplo

001
002
003
004

El byte número 8 es la segunda línea nueva, no la 0línea siguiente.

Lo siguiente le dará el número de líneas completas después de los $bbytes:

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

Informará 2con el bconjunto a 8 e informará 1con el bconjunto a 7.

La ddutilidad, como se usa aquí, leerá el archivo data.iny leerá $bbloques de 1 byte.

Como "icarus" señala correctamente en los comentarios a continuación, el uso bs=1es ineficiente. Es más eficiente, en este caso particular, intercambiar bsy count:

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

Esto tendrá el mismo efecto que el primer ddcomando, pero leerá solo un bloque de $bbytes.

La wcutilidad cuenta nuevas líneas, y una "línea" en Unix siempre termina con una nueva línea. Por lo tanto, el comando anterior aún dirá 2si establece un valor binferior a 12 (la siguiente nueva línea). El resultado que está buscando es, por lo tanto, cualquier número que indique la tubería anterior, más 1.

Obviamente, esto también contará las nuevas líneas aleatorias en la parte de blob binario de su archivo que precede al texto ASCII. Si supiera dónde comienza el bit ASCII, puede agregar skip="$offset"al ddcomando, dónde $offsetestá la cantidad de bytes que se omitirán en el archivo.

Kusalananda
fuente
@don_crisstihead: unknown option -- c
Kusalananda
@Kusalananda Estás usando la cabeza BSD, las opciones son diferentes
Sergiy Kolodyazhnyy
@Serg :-) Soy muy consciente de eso. No sabemos qué OP utiliza, por lo que me quedo con POSIX.
Kusalananda
1
Como mencioné en Q: el recuento de bytes comienza con 0, no con 1, por lo tanto 8 == 0 ...
user367890
@ user367890 En ese caso, use $(( b - 1 )).
Kusalananda
4

Actualmente no existe una herramienta dedicada como esa, aunque se puede hacer con bastante facilidad en 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)

El uso es simple:

line4byte.py <FILE> <BYTE>

Prueba de funcionamiento:

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

Este es un script muy rápido y simple. No comprueba si el archivo está vacío o no, por lo que funciona solo en archivos no vacíos.

Sergiy Kolodyazhnyy
fuente
4

Rastree los bytes vistos y emita el número de línea actual en caso de que el desplazamiento dado esté dentro de la suma:

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

O por fin:

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