2

Windows フォーム C# アプリには、ユーザーがログ データを貼り付けるテキスト ボックスがあり、並べ替えられます。各行を個別に確認する必要があるため、入力を新しい行で分割しますが、100,000 以上の行が多数ある場合、OutOfMemoryException がスローされます。

私のコードは次のようになります。

StringSplitOptions splitOptions = new StringSplitOptions();
if(removeEmptyLines_CB.Checked)
    splitOptions = StringSplitOptions.RemoveEmptyEntries;
else
    splitOptions = StringSplitOptions.None;

List<string> outputLines = new List<string>();

foreach(string line in input_TB.Text.Split(new string[] { "\r\n", "\n" }, splitOptions))
{
    if(line.Contains(inputCompare_TB.Text))
        outputLines.Add(line);
}
output_TB.Text = string.Join(Environment.NewLine, outputLines);

ここで、テキストボックスのテキストを行ごとに分割すると問題が発生しますinput_TB.Text.Split(new string[] { "\r\n", "\n" }

これを行うより良い方法はありますか?最初の X 個のテキストを取得し、新しい行で切り捨てて、すべてが読み取られるまで繰り返すことを考えましたが、これは面倒です。または、より多くのメモリを割り当てる方法はありますか?

ありがとう、ギャレット

アップデート

Attila のおかげで、私はこれを思いつきました。ありがとう

StringReader reader = new StringReader(input_TB.Text);
string line;
while((line = reader.ReadLine()) != null)
{
    if(line.Contains(inputCompare_TB.Text))
        outputLines.Add(line);
}
output_TB.Text = string.Join(Environment.NewLine, outputLines);
4

5 に答える 5

3

Split元のテキストに必要なメモリと、string各行のオブジェクトのオーバーヘッドを複製する必要があります。これによりメモリの問題が発生する場合、入力を処理する信頼できる方法は、一度に 1 行ずつ解析することです。

于 2012-04-30T11:50:39.780 に答える
2

これを行うより良い方法は、一度に 1 行ずつ抽出して処理し、 a を使用StringBuilderして結果を作成することです。

StringBuilder outputTxt = new StringBuilder();
string txt = input_TB.Text;
int txtIndex = 0;
while (txtIndex < txt.Length) {
  int startLineIndex = txtIndex;
GetMore:
  while (txtIndex < txt.Length && txt[txtIndex] != '\r'  && txt[txtIndex] != '\n')) {
    txtIndex++;
  }
  if (txtIndex < txt.Length && txt[txtIndex] == '\r' && (txtIndex == txt.Length-1 || txt[txtIndex+1] != '\n') {
    txtIndex++;
    goto GetMore; 
  }
  string line = txt.Substring(startLineIndex, txtIndex-startLineIndex);
  if (line.Contains(inputCompare_TB.Text)) {
    if (outputTxt.Length > 0)
      outputTxt.Append(Environment.NewLine);
    outputTxt.Append(line); 
  }
  txtIndex++;
} 
output_TB.Text = outputTxt.ToString(); 

先制的なコメント: 誰かがgoto- に反対するだろうが、それはここで必要なものであり、代替手段ははるかに複雑である (reg exp など) か、別のループで goto を偽造するか、continueまたはbreak

a を使用しStringReaderて行を分割すると、よりクリーンなソリューションになりますが、 と の両方新しい行として処理することはできません\r\n\n

StringReader reader = new StringReader(input_TB.Text); 
StringBuilder outputTxt = new StringBuilder();
string compareTxt = inputCompare_TB.Text;
string line; 
while((line = reader.ReadLine()) != null) { 
  if (line.Contains(compareTxt)) {
    if (outputTxt.Length > 0)
      outputTxt.Append(Environment.NewLine);
    outputTxt.Append(line); 
  }
} 
output_TB.Text = outputTxt.ToString(); 
于 2012-04-30T12:11:05.523 に答える
0

各行の文字列を一度に1つずつ作成することで、すべての行と配列の文字列を作成することを回避できます。

var eol = new[] { '\r', '\n' };

var pos = 0;
while (pos < input.Length)
{
    var i = input.IndexOfAny(eol, pos);
    if (i < 0)
    {
        i = input.Length;
    }
    if (i != pos)
    {
        var line = input.Substring(pos, i - pos);

        // process line
    }
    pos = i + 1;
}
于 2012-04-30T11:58:19.280 に答える
0

一方、この記事では、「分割」方式の実装が不十分であることがポイントであると述べています。それを読んで、結論を出してください。

Attilaが言ったように、行ごとに解析する必要があります。

于 2012-04-30T11:58:36.107 に答える
0

大きなテキストファイルでこれを行う唯一の方法は、ファイルを手動で開き、StreamReader. これを行う方法の例を次に示します。

于 2012-04-30T11:51:06.487 に答える