特定の場所に特定の文字を挿入する必要がある巨大なファイルがあります。ファイル全体を再度書き直さずに C# でこれを行う最も簡単な方法は何ですか。
10 に答える
ファイルシステムは、ファイルの途中でのデータの「挿入」をサポートしていません。ソートされた方法で書き込むことができるファイルが本当に必要な場合は、組み込みデータベースの使用を検討することをお勧めします。
SQLiteまたはBerkeleyDBを見てみるとよいでしょう。
ここでも、テキスト ファイルまたはレガシー バイナリ ファイルを操作している可能性があります。その場合、唯一のオプションは、少なくとも挿入ポイントから最後まで、ファイルを書き直すことです。
C# でランダム I/O を実行するには、 FileStreamクラスを調べます。
おそらく、変更を挿入した時点から最後までファイルを書き直す必要があります。常にファイルの最後に書き込み、sort や grep などのツールを使用して、目的の順序でデータを取得するのが最善の方法です。ここでは、バイナリ ファイルではなく、テキスト ファイルについて話していると想定しています。
文字を書き換えずにファイルに文字を挿入する方法はありません。C# では、任意の Stream クラスで実行できます。ファイルが巨大な場合は、C# コード内で GNU Core Utils を使用することをお勧めします。彼らは最速です。以前は、コア ユーティリティを使用して非常に大きなテキスト ファイル (サイズ 4GB、8GB、またはそれ以上など) を処理していました。head、tail、split、csplit、cat、shuf、shred、uniq などのコマンドは、テキスト操作に非常に役立ちます。
たとえば、2 GB のファイルにいくつかの文字を入れる必要がある場合は、split -b BYTECOUNT を使用して、出力をファイルに入れ、新しいテキストを追加し、残りのコンテンツを取得して追加することができます。これはおそらく他のどの方法よりも速いはずです。
それがうまくいくことを願っています。試してみる。
ランダムアクセスを使用してファイルの特定の場所に書き込むことはできますが、テキスト形式で行うことはできず、バイトを直接操作する必要があります。
ファイルシステムがファイルを格納する方法によっては、途中でバイトをすばやく挿入(つまり追加)することが「可能」である可能性があります。リモートで可能な場合は、ファイルシステム自体の低レベルの変更を行うか、ファイルシステム固有のインターフェイスを使用することによってのみ、一度に完全なブロックを実行することが可能になる場合があります。
ファイルシステムは通常、この操作用に設計されていません。挿入をすばやく行う必要がある場合は、より一般的なデータベースが本当に必要です。
アプリケーションによっては、挿入物をまとめることが中間点となるため、ファイルの再書き込みは20回ではなく1回だけです。
挿入ポイントから残りのバイトを常に書き換える必要があります。この点が 0 の場合、ファイル全体を書き換えます。最後のバイトの 10 バイト前の場合は、最後の 10 バイトを書き換えます。
いずれにせよ、「ファイルへの挿入」を直接サポートする機能はありません。しかし、次のコードはそれを正確に行うことができます。
var sw = new Stopwatch();
var ab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";
// create
var fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
fs.Seek(0, SeekOrigin.Begin);
for (var i = 0; i < 40000000; i++) fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);
fs.Dispose();
// insert
fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
byte[] b = new byte[262144];
long target = 10, offset = fs.Length - b.Length;
while (offset != 0)
{
if (offset < 0)
{
offset = b.Length - target;
b = new byte[offset];
}
fs.Position = offset; fs.Read(b, 0, b.Length);
fs.Position = offset + target; fs.Write(b, 0, b.Length);
offset -= b.Length;
}
fs.Position = target; fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);
ファイル IO のパフォーマンスを向上させるには、上記のコードのように「魔法の 2 乗数」で遊んでください。ファイルの作成には、まったく役に立たない 262144 バイト (256KB) のバッファーが使用されます。コードを実行すると、StopWatch の結果からわかるように、挿入用の同じバッファーが「パフォーマンス ジョブ」を実行します。PC でのドラフト テストでは、次の結果が得られました。
作成に 13628.8 ミリ秒、挿入に 3597.0971 ミリ秒。
挿入対象のバイトが 10 であることに注意してください。これは、ファイル全体がほぼ書き換えられたことを意味します。
プロジェクトの範囲に応じて、テキストの各行をファイルと共にテーブル データ構造に挿入することを決定することができます。データベース テーブルのようなもので、いつでも特定の場所に挿入でき、テキスト ファイル全体を毎回読み込み、変更、出力する必要がありません。これは、データが「巨大」であるという事実を考慮したものです。それでもファイルを再作成しますが、少なくともこの方法でスケーラブルなソリューションを作成します。