3

一度に数行のチャンクで(小さい)ファイルを読み取ろうとしていますが、特定のチャンクの先頭に戻る必要があります。

問題は、最初の呼び出しの後です

streamReader.ReadLine();

streamReader.BaseStream.Positionプロパティはファイルの最後に設定されています! バックステージでキャッシングが行われていると仮定しますが、このプロパティには、そのファイルから使用したバイト数が反映されると予想していまし。はい、ファイルには複数の行があります:-)

たとえば、ReadLine()再度呼び出すと、ファイル内の次の行が (自然に) 返されます。これは、以前に によって報告された位置から開始されませんstreamReader.BaseStream.Position

最初の行が終了する実際の位置を見つけて、後でそこに戻るにはどうすればよいですか?

ReadLine() によって返される文字列の長さを追加することによって、手動で簿記を行うことしか考えられませんが、ここでもいくつかの注意事項があります。

  • ReadLine() は、可変長の改行文字を取り除きます ('\n' ですか? "\r\n" ですか? など)。
  • これが可変長文字でうまくいくかどうかはわかりません

...だから今、私の唯一の選択肢は、ファイルの解析方法を再考することだと思われるので、巻き戻す必要はありません。

それが役立つ場合は、次のようにファイルを開きます。

using (var reader = new StreamReader(
        new FileStream(
                       m_path, 
                       FileMode.Open, 
                       FileAccess.Read, 
                       FileShare.ReadWrite)))
{...}

助言がありますか?

4

4 に答える 4

4

行を読む必要があり、前のチャンクに戻る必要がある場合は、読んだ行をリストに保存してみませんか? それは十分に簡単なはずです。

文字列の長さに基づいてバイト単位の長さを計算することに依存するべきではありません-あなた自身が言及している理由: マルチバイト文字、改行文字など.

于 2010-05-28T16:48:32.093 に答える
4

非常に大きなテキスト ファイルの n 番目の行にすばやくアクセスする必要がある場合に、同様の実装を行いました。

streamReader.BaseStream.Positionファイルの終わりを指していた理由は、予想どおり、組み込みのバッファーがあるためです。

各呼び出しから読み取られたバイト数をカウントすることによるブックキーピングはReadLine()、ほとんどのプレーン テキスト ファイルで機能します。ただし、テキスト ファイルに印刷できない制御文字が混在している場合があります。計算されたバイト数が間違っているため、プログラムがその後正しい場所をシークできなくなりました。

私の最終的な解決策は、ラインリーダーを自分で実装することでした。これまでのところうまくいきました。これにより、次のようにいくつかのアイデアが得られるはずです。

using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
    int ch;
    int currentLine = 1, offset = 0;

    while ((ch = fs.ReadByte()) >= 0)
    {
        offset++;

        // This covers all cases: \r\n and only \n (for UNIX files)
        if (ch == 10)
        {
            currentLine++;

            // ... do sth such as log current offset with line number
        }
    }
}

ログに記録されたオフセットに戻るには:

using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
    fs.Seek(yourOffset, SeekOrigin.Begin);
    TextReader tr = new StreamReader(fs);

    string line = tr.ReadLine();
}

また、 にはすでにバッファリング メカニズムが組み込まれて FileStreamいることに注意してください。

于 2010-05-28T17:04:39.877 に答える
2

StreamReaderはこの種の使用法のために設計されていないので、これが必要な場合は、独自のラッパーを作成する必要があると思いますFileStream

于 2010-05-28T16:46:33.663 に答える
1

受け入れられた答えの問題は、ReadLine() が例外に遭遇した場合、たとえば、ReadLine() のときにロギング フレームワークがファイルを一時的にロックしているために、その行が返されなかったため、その行がリストに「保存」されないことです。行。この例外をキャッチした場合、StreamReaders の内部状態とバッファーが最後の ReadLine() から台無しにされ、行の一部しか返されず、その壊れた行を無視してシークできないため、ReadLine() をもう一度再試行することはできません。 OPが見つけたので、最初に戻ります。

真のシーク可能な場所に到達したい場合は、リフレクションを使用して、独自のバッファー内の位置を計算できる StreamReaders プライベート変数に到達する必要があります。ここに見られるグレンジャーのソリューション: StreamReader と seekが機能するはずです。または、他の関連する質問の他の回答が行ったことを実行します:真のシーク可能な場所を公開する独自の StreamReader を作成します (このリンクのこの回答: Tracking the position of the line of a streamreader )。これらは、StreamReader とシークを扱っているときに遭遇した唯一の 2 つのオプションであり、何らかの理由で、ほぼすべての状況でシークの可能性を完全に排除することにしました。

編集:グレンジャーのソリューションを使用しましたが、機能します。GetActualPosition()、次に BaseStream.Position をその位置に設定してから、必ず DiscardBufferedData() を呼び出し、最後に ReadLine() を呼び出すと、その位置から始まる完全な行が取得されます。メソッドで与えられます。

于 2015-11-06T18:25:46.170 に答える