C# を使用してファイルに文字を挿入する方法
-
01-07-2019 - |
質問
巨大なファイルがあり、特定の位置に特定の文字を挿入する必要があります。ファイル全体を再度書き直すことなく、C# でこれを行う最も簡単な方法は何ですか。
他のヒント
おそらく、変更を挿入した時点から最後までファイルを書き直す必要があります。常にファイルの末尾に書き込み、sort や grep などのツールを使用してデータを目的の順序で取り出すのが最善の方法かもしれません。ここではバイナリ ファイルではなくテキスト ファイルについて話していると仮定します。
文字を書き換えずにファイルに挿入する方法はありません。C# では、任意の Stream クラスで実行できます。ファイルが大きい場合は、C# コード内で GNU Core Utils を使用することをお勧めします。彼らは最も速いです。私は以前、コアユーティリティを使用して非常に大きなテキストファイル(サイズが4GB、8GB、またはそれ以上など)を処理していました。head、tail、split、csplit、cat、shuf、shred、uniq などのコマンドは、テキスト操作に非常に役立ちます。
たとえば、2 GB のファイルにいくつかの文字を入れる必要がある場合、split -b BYTECOUNT を使用し、出力をファイルに入れ、新しいテキストをそれに追加し、残りのコンテンツを取得して追加します。これはおそらく他のどの方法よりも高速であるはずです。
うまくいくといいですね。試してみる。
ランダム アクセスを使用してファイルの特定の場所に書き込むことはできますが、テキスト形式では実行できず、バイトを直接操作する必要があります。
新しいデータを書き込む特定の場所がわかっている場合は、BinaryWriter クラスを使用します。
using (BinaryWriter bw = new BinaryWriter (File.Open (strFile, FileMode.Open)))
{
string strNewData = "this is some new data";
byte[] byteNewData = new byte[strNewData.Length];
// copy contents of string to byte array
for (var i = 0; i < strNewData.Length; i++)
{
byteNewData[i] = Convert.ToByte (strNewData[i]);
}
// write new data to file
bw.Seek (15, SeekOrigin.Begin); // seek to position 15
bw.Write (byteNewData, 0, byteNewData.Length);
}
このプロジェクトをご覧ください:Win データインスペクター
基本的に、コードは次のとおりです。
// this.Stream is the stream in which you insert data
{
long position = this.Stream.Position;
long length = this.Stream.Length;
MemoryStream ms = new MemoryStream();
this.Stream.Position = 0;
DIUtils.CopyStream(this.Stream, ms, position, progressCallback);
ms.Write(data, 0, data.Length);
this.Stream.Position = position;
DIUtils.CopyStream(this.Stream, ms, this.Stream.Length - position, progressCallback);
this.Stream = ms;
}
#region Delegates
public delegate void ProgressCallback(long position, long total);
#endregion
DIUtils.cs
public static void CopyStream(Stream input, Stream output, long length, DataInspector.ProgressCallback callback)
{
long totalsize = input.Length;
long byteswritten = 0;
const int size = 32768;
byte[] buffer = new byte[size];
int read;
int readlen = length < size ? (int)length : size;
while (length > 0 && (read = input.Read(buffer, 0, readlen)) > 0)
{
output.Write(buffer, 0, read);
byteswritten += read;
length -= read;
readlen = length < size ? (int)length : size;
if (callback != null)
callback(byteswritten, totalsize);
}
}
プロジェクトの範囲によっては、ファイルにテキストの各行を挿入することもできます。 テーブルのデータ構造。データベースのテーブルのようなもの, こうすることで、いつでも特定の場所に挿入できるようになり、毎回テキスト ファイル全体を読み取ったり、変更したり、出力したりする必要がなくなります。これは、あなたの言うとおり、データが「巨大」であるという事実を考慮したものです。ファイルを再作成することになりますが、少なくともこの方法でスケーラブルなソリューションを作成できます。
ファイルシステムがファイルをどのように保存するかによっては、途中にバイトを素早く挿入する(つまり追加する)ことが「可能」かもしれません。リモートで可能であるとしても、一度にブロック全体を実行することは、ファイルシステム自体の低レベルの変更を行うか、ファイルシステム固有のインターフェイスを使用することによってのみ実現可能です。
ファイルシステムは通常、この操作用に設計されていません。挿入を迅速に行う必要がある場合は、より一般的なデータベースが必要になります。
アプリケーションによっては、中間点として挿入をまとめて、ファイルの書き換えを 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 であることに注意してください。これは、ファイルのほぼ全体が書き換えられたことを意味します。
ファイルの末尾 (文字通り、ファイルの現在のサイズより 4 バイト上) にポインターを置き、ファイルの末尾に挿入されたデータの長さを書き込み、最後に挿入したいデータを書き込んでみてはいかがでしょうか自体。たとえば、ファイルの途中に文字列があり、その文字列の途中に数文字を挿入したい場合は、文字列内の 4 文字の上にファイルの終わりへのポインタを書き込み、次のように書きます。その 4 文字と最初に挿入したい文字を末尾まで入力します。すべてはデータの注文に関するものです。もちろん、これを実行できるのは、ファイル全体を自分で作成している場合、つまり他のコーデックを使用していない場合のみです。