2017-07-06 5 views
0

C#とオブジェクト指向プログラミングの新機能です。私はテキストファイルを解析するアプリケーションを持っています。C#エラー:OutOfMemoryException - 大きなテキストファイルを読み込んで辞書から置換する

アプリケーションの目的は、提供されたテキストファイルの内容を読み取り、一致する値を置き換えることです。

約800 MBから1.2 GBのファイルが入力として提供されると、アプリケーションはSystem.OutofMemoryExceptionエラーでクラッシュします。

調査中、ターゲットプラットフォームをx64に変更することをお勧めするいくつかの回答が出ました。

ターゲットプラットフォームを変更した後も同じ問題が発生します。続き

はコードです:

// Reading the text file 
       var _data = string.Empty; 
       using (StreamReader sr = new StreamReader(logF)) 
       { 
        _data = sr.ReadToEnd(); 
        sr.Dispose(); 
        sr.Close(); 
       } 

       foreach (var replacement in replacements) 
       { 
        _data = _data.Replace(replacement.Key, replacement.Value); 
       } 


       //Writing The text File 
       using (StreamWriter sw = new StreamWriter(logF)) 
       { 
        sw.WriteLine(_data); 
        sw.Dispose(); 
        sw.Close(); 
       } 

エラーポイント

_data = sr.ReadToEnd();

代替品への辞書です。キーには元の単語が含まれ、値には置換する単語が含まれます。

Key要素は、KeyValuePairのValue要素で置き換えられます。

ファイルが読み込まれ、置き換えられ、書き込まれるというアプローチが続いています。

文字列の代わりにStringBuilderを使ってみましたが、アプリケーションがクラッシュしました。

ファイルを1行ずつ読み込み、置き換えて書き換えることでこれを克服できますか?同じことをする効率的かつ迅速な方法は何でしょうか。

アップデート:システムメモリは8 GBで、パフォーマンスを監視すると最大100%のメモリ使用量になります。

@Tim Schmelterの回答がうまくいきます。

ただし、メモリ使用率が90%を超えます。次のコードが原因である可能性があります。

  String[] arrayofLine = File.ReadAllLines(logF); 
      // Generating Replacement Information 
      Dictionary<int, string> _replacementInfo = new Dictionary<int, string>(); 
      for (int i = 0; i < arrayofLine.Length; i++) 
      { 
       foreach (var replacement in replacements.Keys) 
       { 
        if (arrayofLine[i].Contains(replacement)) 
        { 
         arrayofLine[i] = arrayofLine[i].Replace(replacement, masking[replacement]); 
         if (_replacementInfo.ContainsKey(i + 1)) 
         { 
          _replacementInfo[i + 1] = _replacementInfo[i + 1] + "|" + replacement; 
         } 
         else 
         { 
          _replacementInfo.Add(i + 1, replacement); 
         } 
        } 
       } 
      } 

//Creating Replacement Information 
       StringBuilder sb = new StringBuilder(); 
       foreach (var Replacement in _replacementInfo) 
       { 
        foreach (var replacement in Replacement.Value.Split('|')) 
        { 
         sb.AppendLine(string.Format("Line {0}: {1} ---> \t\t{2}", Replacement.Key, replacement, masking[replacement])); 
        } 
       } 

       // Writing the replacement information 
       if (sb.Length!=0) 
       { 
       using (StreamWriter swh = new StreamWriter(logF_Rep.txt)) 
       { 
        swh.WriteLine(sb.ToString()); 
        swh.Dispose(); 
        swh.Close(); 
       } 
       } 
       sb.Clear(); 

置き換えられた行番号が見つかりました。データをメモリに複数回ロードするのを避けるため、これをTimのコードを使ってキャプチャすることができます。

+0

投稿を更新して、ログファイルの最初の数行を含めてください。あなたのマシンにはどれくらいのRAMがありますか? – mjwills

+0

それで、行ごとにそれを読んでみませんか? –

+0

行ごとに読み込み、今はファイルサイズがマシンメモリより大きい場合、ファイルデータ全体をメモリに持ち込みます。 –

答えて

4

ファイルが非常に大きい場合は、この目的のために設計されたMemoryMappedFile(ファイル> 1GB)を試し、ファイルの「ウィンドウ」をメモリに読み込むことができます。しかし、使い方は簡単ではありません。

簡単な最適化では、古いをoverwiteしたい場合は、File.Copy(logF_Temp, logF, true);を使用することができます終わり線で

int lineNumber = 0; 
var _replacementInfo = new Dictionary<int, List<string>>(); 

using (StreamReader sr = new StreamReader(logF)) 
{ 
    using (StreamWriter sw = new StreamWriter(logF_Temp)) 
    { 
     while (!sr.EndOfStream) 
     { 
      string line = sr.ReadLine(); 
      lineNumber++; 
      foreach (var kv in replacements) 
      { 
       bool contains = line.Contains(kv.Key); 
       if (contains) 
       { 
        List<string> lineReplaceList; 
        if (!_replacementInfo.TryGetValue(lineNumber, out lineReplaceList)) 
         lineReplaceList = new List<string>(); 
        lineReplaceList.Add(kv.Key); 
        _replacementInfo[lineNumber] = lineReplaceList; 

        line = line.Replace(kv.Key, kv.Value); 
       } 
      } 
      sw.WriteLine(line); 
     } 
    } 
} 

を行を読んで置き換えることであろう。

+0

あなたは 'vLineNumber'を使用していません。 –

+0

' MemoryMappedFile'についてそれらと投稿のayendeは私をたくさん助けたhttps://ayende.com/blog/search?q=Memory+Mapped+Files – jjchiw

+0

@Tim Schmelterコードが動作します。同じコードを使用して置換情報をキャプチャすることは可能ですか?置き換えられた行番号?私は投稿を更新しました。 – Tango

-1

OutOfMemoryExceptionは、アプリケーションが操作を実行するためにメモリの割り当てを試みて失敗した場合にスローされます。Microsoftのドキュメントによると、以下の操作が潜在的にOutOfMemoryException例外をスローすることができます

ボクシングを(つまり、オブジェクトに値型をラップする)配列 あなたがオブジェクトの無限の数を作成しようとするオブジェクト の作成を作成する 、遅かれ早かれメモリが足りなくなると想定するのはかなり合理的です。

(注:それは彼らが使用されなくなっている決定していない場合ガベージコレクタを忘れないでください作成されたオブジェクトの寿命に応じて、そのうちのいくつかを削除します。)

私が疑うところは、次の行です:

foreach (var replacement in replacements) 
       { 
        _data = _data.Replace(replacement.Key, replacement.Value); 
       } 

これはまもなくまたはそれ以降にメモリが使い果たされることになります。あなたはループした回数をカウントしていますか?

  • が使用可能なメモリを増やしてみてください。
  • 取得するデータの量を減らしてください。
0

ファイルを1行ずつ読み込み、変更された行を別のファイルに追加します。最後に、ソースファイルを新しいファイルに置き換えます(バックアップを作成するかどうか)。

var tmpFile = Path.GetTempFileName(); 
using (StreamReader sr = new StreamReader(logF)) 
{ 
    using (StreamWriter sw = new StreamWriter(tmpFile)) 
    { 
     string line; 
     while ((line = sr.ReadLine()) != null) 
     { 
      foreach (var replacement in replacements) 
       line = line.Replace(replacement.Key, replacement.Value); 

      sw.WriteLine(line); 
     } 
    } 
} 
File.Replace(tmpFile, logF, null);// you can pass backup file name instead on null if you want a backup of logF file 
関連する問題