ホームページ  >  記事  >  バックエンド開発  >  Goで長いテキストファイル内のパターンを含む行を無視する

Goで長いテキストファイル内のパターンを含む行を無視する

WBOY
WBOY転載
2024-02-13 13:57:19986ブラウズ

在 Go 中忽略长文本文件中包含模式的行

php エディター Apple Go 言語では、大きなテキスト ファイルを処理する必要があることがよくあります。場合によっては、特定のパターンを含む行のみに関心があり、他の行は無視されることがあります。幸いなことに、Go では正規表現と bufio.Scanner を使用してこの目標を達成できます。正規表現を使用して行を照合し、スキャナーでファイルを 1 行ずつ実行することで、興味のない行を簡単に除外できます。このヒントにより、効率が向上するだけでなく、コードがより簡潔で読みやすくなります。次に、Go で長いテキスト ファイル内のパターンを含む行を無視する方法を見てみましょう。

質問内容

長いテキストファイル(ASCII保証)のパターンを含む行を無視する機能をgoで実装しようとしています

withoutignorewithignore の関数は両方とも、ファイル名パラメータを入力として受け入れ、*byte.buffer を返します。これは、## の書き込みに使用できます。 #io.writer

withignore

この関数は追加の引数 pattern を受け取り、パターンを含む行をファイルから除外します。この関数は動作しますが、ベンチマークの結果、 を無視せずに よりも 5 倍遅いことが判明しました。何か改善できる方法はあるでしょうか? リーリー ベンチマーク

リーリー

"base64dump.log"

を使用してコマンド ラインから生成できます。 <pre class="brush:golang;toolbar:false;">package main import ( &quot;bufio&quot; &quot;bytes&quot; &quot;io&quot; &quot;log&quot; &quot;os&quot; ) func withoutignore(f string) (*bytes.buffer, error) { rfd, err := os.open(f) if err != nil { log.fatal(err) } defer func() { if err := rfd.close(); err != nil { log.fatal(err) } }() inputbuffer := make([]byte, 1048576) var bytesread int var bs []byte opbuffer := bytes.newbuffer(bs) for { bytesread, err = rfd.read(inputbuffer) if err == io.eof { return opbuffer, nil } if err != nil { return nil, nil } _, err = opbuffer.write(inputbuffer[:bytesread]) if err != nil { return nil, err } } return opbuffer, nil } func withignore(f, pattern string) (*bytes.buffer, error) { rfd, err := os.open(f) if err != nil { log.fatal(err) } defer func() { if err := rfd.close(); err != nil { log.fatal(err) } }() scanner := bufio.newscanner(rfd) var bs []byte buffer := bytes.newbuffer(bs) for scanner.scan() { if !bytes.contains(scanner.bytes(), []byte(pattern)) { _, err := buffer.writestring(scanner.text() + &quot;\n&quot;) if err != nil { return nil, nil } } } return buffer, nil } func main() { // buff, err := withoutignore(&quot;base64dump.log&quot;) buff, err := withignore(&quot;base64dump.log&quot;, &quot;audit&quot;) if err != nil { log.fatal(err) } _, err = buff.writeto(os.stdout) if err != nil { log.fatal(err) } } </pre>

解決策

ascii は保証されているため、

byte

レベルで直接動作できます。 ただし、入力の読み取り中に各バイトで改行文字をチェックし、行内のパターンを再度検索すると、操作は各バイトに適用されます。

一方、各入力バイトをチェックすることなく、入力ブロックを読み取り、テキスト内のパターンの最適化された検索を実行すると、入力バイトごとの演算数を最小限に抑えることができます。

たとえば、boyer-moore 文字列検索アルゴリズム。 Go の組み込み

bytes.index

関数も最適化されています。もちろん、達成される速度は入力データと実際のモードによって異なります。質問で指定された入力について、`bytes.index のパフォーマンスが測定すると大幅に向上しました。 ######プログラム######

ブロック サイズが行の最大長より大幅に長いブロックを読み取る場合、質問のテストでは 1mb が使用されているため、値 >= 64kb がおそらく適切です。

通常、ブロックは改行文字で終了しないため、ブロックの終わりから次の改行文字まで検索し、検索をこのスライスに限定し、残りのデータを次のパスのために記憶します

最後のブロックは必ずしも改行文字で終わるとは限りません
  • 高性能 go 関数
  • bytes.index
  • を使用すると、ブロック内のパターンが発生する場所を見つけることができます
  • 見つかった位置から前後の改行文字を検索します
  • ブロックは対応する行の先頭に出力されます そして、パターンが表示される行の末尾から検索を続けます
  • 検索で他の場所が見つからない場合は、残りの場所を出力します
  • 次のブロックを読み、ファイルの終わりに到達するまで、説明されている手順を再度適用します。
  • 注目に値する
  • 読み取り操作ではブロック サイズよりも少ないデータが返される場合があるため、ブロック サイズのデータ​​が読み取られるまで読み取り操作を繰り返すことが合理的です。
######基準######

最適化されたコードは通常、はるかに複雑ですが、後で説明するように、パフォーマンスも大幅に向上します。 リーリー ここで、最適化されたコード

benchmarktestfilter-8

はフィルターなしの操作よりも約 1.9 倍遅いだけですが、

benchmarktestwithignore-8

メソッドはフィルターなしの比較値より 5.4 倍遅くなります。フィルタリング。 別の観点から見ると、最適化されたコードは、最適化されていないコードより

2.8 倍

高速になります。

######コード######

もちろん、これはあなた自身のテスト用のコードです: リーリー ベンチマーク セクションは次のようになります: リーリー フィルター関数は分割されており、実際の作業は

func filter(reader io.reader, pattern []byte, chunksize int) (*bytes.buffer, error)

で行われます。 単体テストの作成は、リーダーとチャンクサイズを挿入することによって準備または検討されています。これはここでは省略されていますが、インデックスを使用する場合には間違いなく推奨されます。

ただし、ここでのポイントは、パフォーマンスを大幅に向上させる方法を見つけることです。

以上がGoで長いテキストファイル内のパターンを含む行を無視するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はstackoverflow.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。