カスタム区切り文字とC#の非常に大きなフィールド値を使用してテキストを解析する最速の方法は何ですか?
-
19-08-2019 - |
質問
非標準の区切り文字(カンマ/引用符またはタブ区切りではない)を持ついくつかの区切りテキストファイルを処理しようとしています。区切り記号は、区切り記号の間に頻繁に表示されないランダムなASCII文字です。周りを検索した後、.NETのソリューションが私のニーズに合っていないことを発見したようです非常に簡単に数百万文字)。
これは少し極端に思えますが、実際には、一部のレビューソフトウェアがドキュメントの全内容を含むフィールド値を持つことは、電子文書発見(EDD)業界の標準です。参考までに、私は以前にPythonでcsvモジュールを使用して問題なくこれを実行しました。
入力例を次に示します。
Field delimiter =
quote character = þ
þFieldName1þþFieldName2þþFieldName3þþFieldName4þ
þValue1þþValue2þþValue3þþSomeVery,Very,Very,Large value(5MB or so)þ
...etc...
編集: そこで、区切りのあるファイルパーサーをゼロから作成しました。バグが発生する可能性があるため、このソリューションを使用するのは少し疲れています。また、<!> quot; elegant <!> quot;を感じません。または、このようなタスクに対して独自のパーサーを作成する必要があるように修正します。私はまた、とにかくこれのために最初からパーサーを書く必要はないと思う。
解決
File Helpers API を使用します。 .NETとオープンソースです。コンパイルされたILコードを使用して、厳密に型指定されたオブジェクトにフィールドを設定することにより、非常に高いパフォーマンスを実現し、ストリーミングをサポートします。
あらゆる種類のファイルタイプとカスタム区切り文字をサポートしています。 4GBを超えるファイルの読み取りに使用しました。
何らかの理由でうまくいかない場合は、string.splitを使用して1行ずつ読み取ってください:
public IEnumerable<string[]> CreateEnumerable(StreamReader input)
{
string line;
while ((line = input.ReadLine()) != null)
{
yield return line.Split('þ');
}
}
これは、Linqでさえもできるストリーム形式で行を表す単純な文字列配列を提供します;)ただし、IEnumerableは遅延ロードされるため、反復するまでStreamReaderを閉じたり変更したりしないでください(または、ToList / ToArrayなどのフルロード操作が発生しました-ただし、ファイルサイズが指定されている場合は、それを行わないと仮定します!)
これの適切な使用例を次に示します。
using (StreamReader sr = new StreamReader("c:\\test.file"))
{
var qry = from l in CreateEnumerable(sr).Skip(1)
where l[3].Contains("something")
select new { Field1 = l[0], Field2 = l[1] };
foreach (var item in qry)
{
Console.WriteLine(item.Field1 + " , " + item.Field2);
}
}
Console.ReadLine();
これはヘッダー行をスキップし、4番目のフィールドに文字列<!> quot; something <!> quot;が含まれるファイルの最初の2つのフィールドを出力します。ファイル全体をメモリに読み込まずにこれを行います。
他のヒント
Windowsおよび高性能I / Oは、 IO完了ポート。ケースで動作させるには、余分な配管作業が必要になる場合があります。
これは、C#/。NETを使用することを理解しており、ジョーダフィー
18)Don <!>#8217; t管理対象でWindows非同期プロシージャコール(APC)を使用 コード。
難しい方法を学ばなければなりませんでした;)が、APCの使用を除外して、IOCPが唯一の正気なオプションです。また、ソケットサーバーでよく使用される他の多くのタイプのI / Oもサポートします。
実際のテキストの解析に関しては、一部の合理化されたストリームの使用に関するEric Whiteのブログ。
メモリマップファイル( msdnポイントの組み合わせを使用する傾向があります。ここで.NETラッパーに)、シンプルなインクリメンタル解析を行い、レコード/テキスト行(またはその他)のIEnumerableリストに戻ります
いくつかのフィールドは非常に大きいと言いますが、それらをすべてメモリに読み込もうとすると、問題が発生する可能性があります。 8K(または小さなチャンク)でファイルを読み取り、現在のバッファーを解析し、状態を追跡します。
解析しているこのデータをどうしようとしていますか?何か探していますか?あなたはそれを変えていますか?
カスタムパーサーを作成しても問題は発生しません。要件は、BCLによって既に提供されているものとは十分に異なるように見えるので、すぐに進めてください。
<!> quot; Elegance <!> quot;明らかに主観的なものです。私の意見では、パーサーのAPIが標準BCL <!> quot; reader <!> quot; -type APIのように見えて動作する場合、それはかなり<!> quot; elegant <!> quot;です。
大きなデータサイズについては、一度に1バイトを読み取ってパーサーを動作させ、単純なステートマシンを使用して何をすべきかを判断します。ストリーミングとバッファリングは、基になるFileStream
クラスに任せます。パフォーマンスとメモリ消費に問題はありません。
このようなパーサークラスの使用例:
using(var reader = new EddReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)) {
// Read a small field
string smallField = reader.ReadFieldAsText();
// Read a large field
Stream largeField = reader.ReadFieldAsStream();
}
これは大規模な入力の問題に対処する助けにはなりませんが、構文解析の問題の可能な解決策には、区切り文字を提供する戦略パターンを使用するカスタムパーサーが含まれる場合があります。