21

その中にいくつかの「レコード」を含むテキストファイルがあります。各レコードには、名前とデータとしての番号のコレクションが含まれています。

ファイルを読み取り、すべてのレコードの名前のみを表示し、ユーザーが必要なレコードデータを選択できるようにするクラスを構築しようとしています。

初めてファイルを調べるときは、ヘッダー名のみを読み取りますが、ヘッダーが存在するファイル内の「位置」を追跡できます。ユーザーが要求した後、各レコードの先頭を探すために、テキストファイルにランダムアクセスする必要があります。

ファイルが大きすぎて、アプリケーションの他のメモリ要求とともにメモリ(1GB以上)に完全に読み込むことができないため、この方法で行う必要があります。

これを実現するために.NETStreamReaderクラスを使用してみました(これは非常に使いやすい「ReadLine」機能を提供しますが、ファイルの実際の位置をキャプチャする方法はありません(BaseStreamプロパティの位置はクラスが使用するバッファ)。

.NETでこれを行う簡単な方法はありませんか?

4

9 に答える 9

13

いくつかの適切な回答が提供されていますが、非常に単純なケースで機能するソース コードが見つかりませんでした。ここにあるのは、私が探し回るのに費やした時間を他の誰かが節約できることを願ってです.

私が言及する「非常に単純なケース」は、テキスト エンコーディングが固定幅で、行末文字がファイル全体で同じであるというものです。このコードは、私の場合 (ログ ファイルを解析していて、ファイルを先にシークしてから戻る必要がある場合があります) でうまく機能します。 、および ReadLine()) のみをオーバーライドするため、おそらくコードを追加する必要があります...しかし、それは妥当な出発点だと思います。

public class PositionableStreamReader : StreamReader
{
    public PositionableStreamReader(string path)
        :base(path)
        {}

    private int myLineEndingCharacterLength = Environment.NewLine.Length;
    public int LineEndingCharacterLength
    {
        get { return myLineEndingCharacterLength; }
        set { myLineEndingCharacterLength = value; }
    }

    public override string ReadLine()
    {
        string line = base.ReadLine();
        if (null != line)
            myStreamPosition += line.Length + myLineEndingCharacterLength;
        return line;
    }

    private long myStreamPosition = 0;
    public long Position
    {
        get { return myStreamPosition; }
        set
        {
            myStreamPosition = value;
            this.BaseStream.Position = value;
            this.DiscardBufferedData();
        }
    }
}

以下は、PositionableStreamReader の使用方法の例です。

PositionableStreamReader sr = new PositionableStreamReader("somepath.txt");

// read some lines
while (something)
    sr.ReadLine();

// bookmark the current position
long streamPosition = sr.Position;

// read some lines
while (something)
    sr.ReadLine();

// go back to the bookmarked position
sr.Position = streamPosition;

// read some lines
while (something)
    sr.ReadLine();
于 2009-05-28T15:34:06.503 に答える
8

FileStreamにはseek()メソッドがあります。

于 2008-11-05T16:15:33.140 に答える
5

StreamReaderの代わりにSystem.IO.FileStreamを使用できます。どのファイルに含まれているのか(たとえばエンコーディング)が正確にわかっている場合は、StreamReaderの場合と同様にすべての操作を実行できます。

于 2008-11-05T16:15:17.960 に答える
5

データ ファイルの書き方に柔軟性があり、テキスト エディターの使いやすさが少し劣っても構わない場合は、BinaryWriter を使用してレコードを書き込むことができます。

using (BinaryWriter writer = 
    new BinaryWriter(File.Open("data.txt", FileMode.Create)))
{
    writer.Write("one,1,1,1,1");
    writer.Write("two,2,2,2,2");
    writer.Write("three,3,3,3,3");
}

次に、BinaryReader の ReadString メソッドを使用できるため、各レコードの最初の読み取りは簡単です。

using (BinaryReader reader = new BinaryReader(File.OpenRead("data.txt")))
{
    string line = null;
    long position = reader.BaseStream.Position;
    while (reader.PeekChar() > -1)
    {
        line = reader.ReadString();

        //parse the name out of the line here...

        Console.WriteLine("{0},{1}", position, line);
        position = reader.BaseStream.Position;
    }
}

BinaryReader はバッファリングされないため、適切な位置に格納して後で使用できます。唯一の面倒は、名前を行から解析することです。これは、とにかく StreamReader で行う必要がある場合があります。

于 2008-11-05T22:58:08.193 に答える
2

エンコーディングは固定サイズですか (ASCII や UCS-2 など)? もしそうなら、(あなたが見た文字の数に基づいて) 文字インデックスを追跡し、それに基づいてバイナリ インデックスを見つけることができます。

それ以外の場合は、いいえ-基本的に、バイナリインデックスを覗くことができる独自の StreamReader 実装を作成する必要があります。StreamReader がこれを実装していないのは残念です。同意します。

于 2008-11-05T16:16:00.237 に答える
1

FileHelpers ライブラリのランタイム レコード機能が役立つと思います。http://filehelpers.sourceforge.net/runtime_classes.html

于 2008-11-05T17:04:17.863 に答える
1

興味がありそうなアイテムをいくつか。

1) 行の長さが固定の文字セットである場合、文字セットが可変サイズ (UTF-8 など) の場合、これは必ずしも有用な情報ではありません。そのため、文字セットを確認してください。

2) BaseStream.Position 値を使用して、StreamReader からファイル カーソルの正確な位置を確認できます。最初にバッファを Flush() します (これにより、現在の位置が強制的に次の読み取りが開始される場所になります。最後の読み取りの 1 バイト後)。バイト読み取り)。

3) 各レコードの正確な長さが同じ文字数であり、文字セットが固定幅文字を使用することが事前にわかっている場合 (したがって、各行の長さは同じバイト数です)、FileStream を行のサイズに一致する固定バッファサイズと、各読み取りの最後にあるカーソルの位置は、強制的に次の行の先頭になります。

4) 行が同じ長さ (ここではバイト単位であると仮定) の場合、単純に行番号を使用せず、行サイズ x 行番号に基づいてファイル内のバイトオフセットを計算しない特定の理由はありますか?

于 2010-03-29T13:57:20.130 に答える
0

ファイルが「大きすぎます」と確信していますか?あなたはそれをそのように試しましたか、そしてそれは問題を引き起こしましたか?

大量のメモリを割り当て、現在それを使用していない場合、Windowsはそれをディスクにスワップアウトするだけです。したがって、「メモリ」からアクセスすることで、ディスク上のファイルへのランダムアクセスという目的を達成できます。

于 2008-11-05T16:15:24.717 に答える
0

この正確な質問は、2006 年にここで尋ねられました: http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic40275.aspx

概要:

「問題は、StreamReader がデータをバッファリングするため、BaseStream.Position プロパティで返される値が、実際に処理された行より常に先にあることです。」

ただし、「ファイルが固定幅のテキストエンコーディングでエンコードされている場合、読み取られたテキストの量を追跡し、それに幅を掛けることができます」

そうでない場合は、FileStream を使用して一度に 1 文字を読み取るだけで、BaseStream.Position プロパティが正しいはずです。

于 2008-11-05T17:44:43.053 に答える