2009-04-30 1 views
6

〜120 MBのプレーンテキストのCSVファイルを読み込むためにC#を使用しています。当初、私は行ごとにそれを読むことによって解析しましたが、最近、ファイルの内容全体をメモリに最初に読み込むことは、何倍も高速でした。 CSVにはカンマが引用符で埋め込まれているため、解析はすでにかなり遅いです。つまり、正規表現の分割を使用する必要があります。私はそれぞれの行を含む配列を取得するには、改行文字の文字列分割を行い、メモリに全体の内容を読んだ後、解析を行うために120 MBのCSVファイルのString.Split()に.NET System.OutOfMemoryExceptionがあります

string[] fields = Regex.Split(line, 
@",(?!(?<=(?:^|,)\s*\x22(?:[^\x22]|\x22\x22|\\\x22)*,) 
(?:[^\x22]|\x22\x22|\\\x22)*\x22\s*(?:,|$))"); 
// from http://regexlib.com/REDetails.aspx?regexp_id=621 

:これは確実に動作します私が見つけた唯一のものです。しかし、120 MBのファイルでこれを行うと、System.OutOfMemoryExceptionが得られます。コンピュータに4GBのRAMが搭載されていると、メモリが急激に使い果たされるのはなぜですか?複雑なCSVをすばやく解析するためのより良い方法はありますか?

答えて

7

基本的に任意のサイズの割り当てに対してOutOfMemoryExceptionを取得できます。あなたが本当に要求されたサイズの連続したメモリを要求しているメモリの一部を割り当てるとき。それが尊重できない場合は、OutOfMemoryExceptionが表示されます。

また、64ビットWindowsを実行していない限り、4 GBのRAMは2 GBのカーネルスペースと2 GBのユーザースペースに分割されるため、.NETアプリケーションはデフォルトあたり2 GB以上アクセスできません。

.NETで文字列演算を実行すると、.NET文字列が不変であるために一時的な文字列が多数作成される危険性があります。したがって、メモリ使用率が大幅に上昇することがあります。

+0

文字列はコンピュータサイエンスの謎の子です。必要な悪い、しかし私はまだ誰かがより良い方法を理解することを望む! –

4

多くの連続したメモリを持つ単一のオブジェクトを割り当てることはできないかもしれません。ストリーミングはこれを行う普通の方法ですが、それは遅いかもしれません(通常はそれほど遅くならないとは思いますが)。

妥協案として、ファイル全体の一部ではなく、StreamReader.ReadBlock()のような機能を持ち、順番に各部分を処理します。

0

CLR profilerを実際に使用してください。システムRAM以外のメモリ制限がある可能性があります。たとえば、これがIISアプリケーションの場合、メモリはアプリケーションプールによって制限されます。

このプロファイル情報を使用すると、最初に試みたCSVファイルのストリーミングなど、よりスケーラブルな手法を使用する必要があることがあります。

5

ファイル全体が文字列に読み込まれている場合は、おそらくStringReaderを使用してください。

StringReader reader = new StringReader(fileContents); 
string line; 
while ((line = reader.ReadLine()) != null) { 
    // Process line 
} 

これは、コンテンツがすでにメモリにあるという違いを持つファイルからのストリーミングとほぼ同じです。

編集

をテストした後、処理はline.Lengthと長さ変数をインクリメントから成って140メガバイトのファイルと上記試みました。これは私のコンピュータで約1.6秒かかった。この後私は以下を試した:

System.IO.StreamReader reader = new StreamReader("D:\\test.txt"); 
long length = 0; 
string line; 
while ((line = reader.ReadLine()) != null) 
    length += line.Length; 

結果は約1秒であった。

もちろん、ネットワークドライブから読み取っている場合や、処理がハードドライブが別の場所を探すのに十分な時間がかかる場合は、マイレージが異なる場合があります。しかし、FileStreamを使用してファイルを読み込んでいて、バッファリングしていない場合も同様です。 StreamReaderは、読み上げを大幅に向上させるバッファリングを提供します。

+0

これは、実際にファイルを最初の場所の文字列に読み込むことができれば非常に良い答えです。少なくとも現時点では可能なように聞こえます。多くのマシンがすぐに120MBのファイルを読み込もうとしなかった場合(または時には失敗し、別の時に働いた場合)、私は驚くことはありません。 – mquander

8

必要がない限り、独自のパーサをロールしないでください。あなたはボンネットの下に見て、他の誰かがそれをしない方法を見ることができます何もない場合は

A Fast CSV Reader

:私はこの1つで運を持っていました。

+1

+1これは大きなCSVファイルの解析にも使用しました。 – Wayne

+1

+1私から。私の経験では、SébastienLorionのCSVリーダーは効率的で柔軟性があり、堅牢です。それはすぐに120MBのファイルを噛む必要があります。 – LukeH

0

ヒープではなくスタック上のメモリが不足しています。

一度に120MBを処理するのではなく、より管理しやすい "チャンク(chunk)"データで入力を処理するように、アプリケーションの再調整を試みることができます。

+0

文字列は、スタックではなくヒープ上に割り当てられます。 int/byte/double /などのプリミティブのみがスタックimrに割り当てられます。 –

+0

@確信していない:あなたは正しい。しかし、プログラムスタックがいっぱいになる可能性のあるさまざまな状況があります。問題のシステムが十分な物理的記憶を持っていることを考えると、これはおそらくそのようなケースの1つであると私は考えています。 =) – Garrett

+0

スタックがいっぱいになると、StackOverflowExceptionが発生し、OutOfMemoryExceptionは発生しません。後者は常にGCヒープ上の不十分なメモリを示すために使用されます。 –

1

他のポスターと同様に、OutOfMemoryは、要求されたサイズの連続したメモリチャンクを見つけることができないためです。

しかし、行ごとに解析するのは、一度にすべてを読み取ってから処理するよりも数倍速いと言います。

while(! file.eof()) 
{ 
    string line = file.ReadLine(); 
    ProcessLine(line); 
} 

あなたが代わりにあなたのストリームが書き込み(によって記入されたストリーミングを、使用する必要がありますが)、代替から呼び出す:あなたは(擬似コードで)ブロッキングを行うことは例えば、読み込みの単純なアプローチを追求した場合にのみ意味がありますあなたのProcessLine()が何をしても読み込まれたファイルはブロックされません。その逆もあります。これは、ファイル全体を一度に読み取ってから処理を実行することと並行して行う必要があります。

+0

マルチスレッドアプローチのコード例を教えてください。私はそれを素朴な方法でやっていました。なぜそれが大きな問題になるのか理解しています。 –

+0

.Netには、ビルトインの非同期ファイルの読み込みと書き込みがあり、BeginRead()呼び出しが適しています。次のGoogleの検索結果には、多くの例があります。http://www.google.com/search?q=.net+asynchronous+file –

0

私はここでほとんどの皆さんに同意します。ストリーミングを使用する必要があります。

これまでに誰かが言っているかどうかはわかりませんが、あなたはexstentionメソッドを見てください。

そして、私は、確かに、ダウン手、.NET/CLR上で最高のCSV分割手法は、その技術が私を生成 this one

知っている+ 10ギガバイトのXML出力のexstensive入力フィルタとすべてを含む入力CSV、から、私が見た他の何ものよりも速く。

+0

ああ、ストリーミング> RAMに何があってもバッファリングされます。 あなたが4GIGを持っていて、2GIGの入力を読み込んだら、ロード時間とあなたのVMサブシステムのスラッシングとページテーブルの大規模なサイズがあなたのCPUキャッシュなどを食うだけであると考えてください。 ..小さくて、管理しやすいワークスペースを維持するために、あなたのキャッシュは "熱く"、あなたのすべてのCPU時間は、システム負荷の大規模なフラクシヨンではなく、手元にあるタスクに専念しています... – RandomNickName42

0

チャンクをバッファに読み込んで処理する必要があります。その後、別のチャンクなどを読んでください。

これを効率的に実行するライブラリが多数あります。私はCsvHelperと呼ばれるものを維持しています。カンマや行末がフィールドの途中にある場合など、扱う必要があるエッジケースが数多くあります。

関連する問題