4

大きなテキスト ファイル (>2GB) があります。現在、Filestream を使用して 1kb のチャンクでファイルを読み取っています。各チャンクで行数を数え、この数を使用して、行を削除する必要があるファイル内の位置を見つけました。

たとえば、削除したい行のバイト位置が 4097 の場合、C# で 4097 から始まる行の文字を \n 文字に到達するまで削除する方法はありますか。

Filestream.Seek() メソッドを見て、削除位置に直接移動しました。しかし、さらに先に進む方法がわかりません。

これは大きなファイルなので、システムとメモリの多くのスペースを消費する別のファイルを作成したくありません。新しいファイルを作成せずに行を削除するために使用できる効率的な方法はありますか?

提案や助けをいただければ幸いです。

前もって感謝します!

4

2 に答える 2

1

ファイルを短くする唯一の方法は、実際にファイルの一部をコピーしてからスキップして、残りをコピーすることだと思います。本当にその場でそれを行う必要がある場合は、何らかの形式の論理的削除を選択できます。たとえば、LFのみを使用してテキストファイルの新しい行をエンコードできます(これはWindowsのデフォルトではなく、代わりにペアのCR-LFを使用します)。これはおそらく8ビットASCIIであり、次のようなものを選択する必要があります。 :

    public static void LogicalEraseLine(string filename, int toDel)
    {
        FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite);

        fs.Seek(toDel, SeekOrigin.Current);
        int c;

        while ((c = fs.ReadByte()) != -1)
        {
            if (c == '\n')
            {
                break;
            }
            else
            {
                fs.Seek(-1, SeekOrigin.Current);
                fs.WriteByte((byte)'\n');
            }
        }

        fs.Close();
    }

toDelは、削除する行のインデックスではなく、削除する最初の文字のインデックスであることに注意してください。このコードは、toDelの文字と行の終わりの間のすべての文字を同じ数の空の行に置き換えるだけです。次に、ファイルを別のファイルにコピーするために別の関数が必要になりますが、空の行はすべてスキップします。このクリーンアップは、将来の都合の良いときにいつでも実行できます。ただし、実際のアルゴリズムでは、ファイル内の多くの空白行に対処できる必要があります。また、ファイルをチャンクで読み取る必要があるのは正しいですが、この例に示されている基本的な考え方は、その場合にも適用できます。

編集 この機能を使用して、論理的に削除された行を消去できます。

    public static void Cleanup (string filename)
    {
        FileStream input = new FileStream(filename, FileMode.Open, FileAccess.Read);
        FileStream output = new FileStream(filename + ".tmp", FileMode.Create, FileAccess.Write);

        bool emptyLine = true;
        int c;

        while ((c = input.ReadByte()) != -1)
        {
            if (c == '\n')
            {
                if (!emptyLine)
                {
                    output.WriteByte((byte)c);
                    emptyLine = true;
                }
            }
            else
            {
                output.WriteByte((byte)c);
                emptyLine = false;
            }
        }

        input.Close();
        output.Close();

        File.Delete (filename);
        File.Copy(filename + ".tmp", filename);
        File.Delete(filename + ".tmp");
    }

また、ファイルを削除するときは、細心の注意を払い、問題が発生する可能性のあるすべてのものを再確認することをお勧めします

編集 最初のアルゴリズムは、まだファイル全体を読んでいたので、ちょっと意味がありませんでした。今では理にかなっています。

于 2013-03-03T23:10:59.843 に答える
0

大きなファイルを処理する最も効率的な方法は、メモリ マップト ファイルを使用することです。その利点は、ファイル全体を読み取り、変更してから再度書き込む必要がなく、データの重要な部分を変更するだけでよいことです。4097 をオフセットとして設定し、約 100 KB をロードします。MSDN のこの例は、開始に役立つはずです。

long offset = 0x10000000;  
long length = 0x20000000; // 512 megabytes 

// Create the memory-mapped file. 
using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))
{
    // Create a random access view, from the 256th megabyte (the offset) 
    // to the 768th megabyte (the offset plus length). 
    using (var accessor = mmf.CreateViewAccessor(offset, length))
    {
        int colorSize = Marshal.SizeOf(typeof(MyColor));
        MyColor color;

        // Make changes to the view. 
        for (long i = 0; i < length; i += colorSize)
        {
            accessor.Read(i, out color);
            color.Brighten(10);
            accessor.Write(i, ref color);
        }
    }
}
于 2013-03-03T22:58:34.610 に答える