.NET を使用してファイル内の変更をリアルタイムで読み取る
-
01-07-2019 - |
質問
頻繁に (1 分あたり約 20 ~ 30 回) 更新される .csv ファイルがあります。新しく追加された行がファイルに書き込まれたらすぐにデータベースに挿入したいと考えています。
の ファイルシステムウォッチャー クラスはファイル システムの変更通知をリッスンし、指定されたファイルに変更があるたびにイベントを発生させることができます。問題は、FileSystemWatcher が (私の知る限り) どの行が追加または削除されたかを正確に判断できないことです。
これらの行を読み取る 1 つの方法は、変更間の行数を保存して比較し、最後の変更と最後から 2 番目の変更の違いを読み取ることです。ただし、私はよりクリーンな (おそらくよりエレガントな) ソリューションを探しています。
解決
非常に似たようなことを書いたことがあります。FileSystemWatcher を使用して、変更に関する通知を取得しました。次に、FileStream を使用してデータを読み取りました (ファイル内の最後の位置を追跡し、新しいデータを読み取る前にそこまでシークします)。次に、読み取ったデータをバッファに追加すると、完全な行が自動的に抽出され、UI に出力されます。
注記:"this.MoreData(..) はイベントであり、そのリスナーは前述のバッファに追加し、完全な行抽出を処理します。
注記:すでに述べたように、これは、変更が常にファイルへの追加である場合にのみ機能します。削除すると問題が発生します。
お役に立てれば。
public void File_Changed( object source, FileSystemEventArgs e )
{
lock ( this )
{
if ( !this.bPaused )
{
bool bMoreData = false;
// Read from current seek position to end of file
byte[] bytesRead = new byte[this.iMaxBytes];
FileStream fs = new FileStream( this.strFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
if ( 0 == this.iPreviousSeekPos )
{
if ( this.bReadFromStart )
{
if ( null != this.BeginReadStart )
{
this.BeginReadStart( null, null );
}
this.bReadingFromStart = true;
}
else
{
if ( fs.Length > this.iMaxBytes )
{
this.iPreviousSeekPos = fs.Length - this.iMaxBytes;
}
}
}
this.iPreviousSeekPos = (int)fs.Seek( this.iPreviousSeekPos, SeekOrigin.Begin );
int iNumBytes = fs.Read( bytesRead, 0, this.iMaxBytes );
this.iPreviousSeekPos += iNumBytes;
// If we haven't read all the data, then raise another event
if ( this.iPreviousSeekPos < fs.Length )
{
bMoreData = true;
}
fs.Close();
string strData = this.encoding.GetString( bytesRead );
this.MoreData( this, strData );
if ( bMoreData )
{
File_Changed( null, null );
}
else
{
if ( this.bReadingFromStart )
{
this.bReadingFromStart = false;
if ( null != this.EndReadStart )
{
this.EndReadStart( null, null );
}
}
}
}
}
他のヒント
そうです、FileSystemWatcher はファイルの内容について何も知りません。変わったかどうかなどを教えてくれます。しかし、何が変わったのかではありません。
ファイルに追加するだけですか?投稿からは、行が追加されたのか、それとも削除できるのかが少し不明瞭でした。追加されていると仮定すると、解決策は非常に簡単です。そうでない場合は、いくつかの比較を行うことになります。
NTFS Change Journal などを使用する必要があると思います。
変更ジャーナルは、NTFSによって使用され、ボリューム上のファイルに行われたすべての変更の永続的なログを提供します。各ボリュームについて、NTFSは変更ジャーナルをに使用します 追加、削除、および変更されたファイルに関する情報を追跡する。変更ジャーナルは、特定の名前空間の変更を決定するためのタイムスタンプまたはファイル通知よりもはるかに効率的です。
見つけることができます TechNet の説明. 。.NET で PInvoke を使用する必要があります。
現在のテキストが十分に小さい場合はメモリ内に保持し、diff アルゴリズムを使用して新しいテキストと前のテキストが変更されたかどうかを確認します。この図書館、 http://www.mathertel.de/Diff/, 、何かが変わったことだけでなく、何が変わったかもわかります。したがって、変更されたデータをデータベースに挿入できます。
思いつきでは、最後にわかっているファイル サイズを保存できるはずです。ファイル サイズを確認し、変更された場合はリーダーを開きます。
次に、最後のファイル サイズまでリーダーをシークし、そこから読み取りを開始します。
FileSystemWatcher については正しいです。作成、変更、削除などをリッスンできます。イベントは発生しますが、イベントを発生させたファイルより深くはわかりません。
ファイル自体を制御できますか?モデルを少し変更して、ファイルをバッファーのように使用することができます。ファイルを 1 つではなく 2 つ用意します。1 つはステージング、もう 1 つは処理されたすべての出力の合計です。「バッファ」ファイルからすべての行を読み取り、処理し、処理されたすべての行の合計である別のファイルの末尾にそれらを挿入します。次に、処理した行を削除します。こうすることで、ファイル内のすべての情報が処理待ちになります。問題は、システムが書き込み以外の場合 (つまり、行も削除します)その場合は機能しません。