バイトオフセットから行番号を取得する


12

ファイルのバイトオフセットがある。

このバイトの行番号を提供するツールはありますか?

  • 最初のバイトは1ではなく0です。
  • 1から始まる行番号。
  • ファイルには、プレーンテキスト、「バイナリ」ブロブ、マルチバイト文字などの両方を含めることができます。しかし、興味があるセクション:ファイルの終わりにはASCIIしかありません。

例、ファイル:

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

8私にラインを与えるバイトオフセットを持っています3

行番号を見つけるためにこのようなものを使用できると思います:

 a。tail -c+(offset + 1) file | wc -l、ここ+1tailは1から数えます
 。wc -l file
 c。次にtail -n+num どこnuma - b + 1

しかし...私にnum直接与えることができるかなり一般的なツールはありますか?


編集、エラー:またはより明白な:

head -c+offset file | wc -l

2
バイナリファイルには行がありません。
クサラナンダ

@Kusalananda:このコンテキストの行は、0x0aバイトで区切られたデータです。
user367890 2017年

3
おそらくあなたが求めていることではありませんが、Vimにはその機能があります。これは、1からのオフセットをカウントし、そう::echo byte2line(offset+1)
桂佐藤

@SatoKatsura:はい、ありがとうございます。最初にvimで試しました。しかしvim -bvim+ + set binary+ファイルを開いても破損していました。(ああ、突然、どのプラグインがそれを台無しにしたか思い出します)。しかし、とにかく、私はこれをバッチで使用し、さまざまなスクリプトと組み合わせて使用​​するため、Vimは早期に放棄されました。とにかく+1。
user367890 2017年

@ user367890バイナリファイルは0xaどこにでも置くことができます。バイナリファイルの行の概念は無意味です。
user207421 2017

回答:


14

あなたの例では、

001
002
003
004

バイト番号8は2番目の改行であり0、次の行ではありません。

以下は、$bバイトの後の完全な行数を示します。

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

それは報告されます2b8にセットし、それが報告されます1b7にセット。

ddユーティリティ、それはここで使われている方法は、ファイルから読み込まれますdata.in、と読みます$bサイズ1バイトのブロックを。

「イカルス」が以下のコメントで正しく指摘しているように、使用bs=1は非効率的です。この特定のケースでは、スワップbscount

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

これは最初のddコマンドと同じ効果があり$bますが、バイトの1ブロックのみを読み取ります。

wcユーティリティは、改行をカウントし、Unixの内、「行」は、常に改行で終了します。したがって、上記のコマンドは、12未満(次の改行)に2設定bした場合でも通知します。したがって、探している結果は、上記のパイプラインレポートの数に1を加えたものになります。

これは明らかに、ASCIIテキストの前にあるファイルのバイナリblob部分のランダムな改行もカウントします。ASCIIビットの開始位置がわかっている場合はskip="$offset"ddコマンドに追加でき$offsetます。ここで、はファイルにスキップするバイト数です。


@don_crisstihead: unknown option -- c
Kusalananda

@Kusalananda BSDヘッドを使用していますが、オプションは異なります
Sergiy Kolodyazhnyy 2017年

@Serg :-)私はそれをよく知っています。OPが何を使用するのかわからないので、私はPOSIXを使用しています。
クサラナンダ

1
Qで述べたように、バイトカウントは1ではなく0で始まるため、8 == 0 ...
user367890

@ user367890その場合は、を使用してください$(( b - 1 ))
クサラナンダ

4

現在のところ、そのような専用のツールはありませんが、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)

使い方は簡単です:

line4byte.py <FILE> <BYTE>

テスト走行:

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

これは非常に素早く簡単なスクリプトです。ファイルが空であるかどうかはチェックしないため、空でないファイルでのみ機能します。


4

指定されたオフセットが合計内にある場合、表示されたバイトを追跡し、現在の行番号を出力します。

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

または長々と:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.