2

200MB を超える大きなファイルがあります。ファイルは外部の CSV ファイルですが、残念ながら\r\n、新しい行を定義するために使用されるように、ファイルを 1 行ずつ読み取ることはできません。

現在、私はこのアプローチを使用してすべての行を読んでいます:

var file = File.ReadAllText(filePath, Encoding.Default);
var lines = Regex.Split(file, @"\r\n");

for (int i = 0; i < lines.Length; i++)
{
    string line = lines[i];
    ...
}

これを最適化するにはどうすればよいですか?225MB のファイルで ReadAllText を呼び出した後、プロセスは 1GB 以上の RAM を使用しています。私の場合、\r\nパターンを使用してファイルを分割する必要がある場合、ストリーミング アプローチを使用することは可能ですか?

EDIT1 : File.ReadLinesStreamReader を使用したソリューションは、ファイル内の各行が 1 行として認識されるため、機能しません。\r\nパターンを使用してファイルを分割する必要があります。私のコードを使用してファイルを読み取ると、758.371 行 (これは正しい) になりますが、通常の行数は 150 万を超えます。

解決

public static IEnumerable<string> ReadLines(string path)
{
    const string delim = "\r\n";

    using (StreamReader sr = new StreamReader(path))
    {
        StringBuilder sb = new StringBuilder();

        while (!sr.EndOfStream)
        {
            for (int i = 0; i < delim.Length; i++)
            {
                Char c = (char)sr.Read();
                sb.Append(c);

                if (c != delim[i])
                    break;

                if (i == delim.Length - 1)
                {
                    sb.Remove(sb.Length - delim.Length, delim.Length);
                    yield return sb.ToString();
                    sb = new StringBuilder();
                    break;
                }
            }
        }

        if (sb.Length>0)
            yield return sb.ToString();
    }
}
4

5 に答える 5

6

ファイル全体をメモリにロードする代わりに、whichreturnsFile.ReadLinesを使用できます。IEnumerable<string>

foreach(var line in File.ReadLines(@filePath, Encoding.Default)
                        .Where(l => !String.IsNullOrEmpty(l)))
{
}
于 2012-10-26T11:23:41.723 に答える
4

StreamReaderを使えば簡単です。

using (StreamReader sr = new StreamReader(path)) 
 {
      foreach(string line = GetLine(sr)) 
      {
           //
      }
 }


    IEnumerable<string> GetLine(StreamReader sr)
    {
        while (!sr.EndOfStream)
            yield return new string(GetLineChars(sr).ToArray());
    }

    IEnumerable<char> GetLineChars(StreamReader sr)
    {
        if (sr.EndOfStream)
            yield break;
        var c1 = sr.Read();
        if (c1 == '\\')
        {
            var c2 = sr.Read();
            if (c2 == 'r')
            {
                var c3 = sr.Read();
                if (c3 == '\\')
                {
                    var c4 = sr.Read();
                    if (c4 == 'n')
                    {
                        yield break;
                    }
                    else
                    {
                        yield return (char)c1;
                        yield return (char)c2;
                        yield return (char)c3;
                        yield return (char)c4;
                    }
                }
                else
                {
                    yield return (char)c1;
                    yield return (char)c2;
                    yield return (char)c3;
                }
            }
            else
            {
                yield return (char)c1;
                yield return (char)c2;
            }
        }
        else
            yield return (char)c1;
    }
于 2012-10-26T11:24:25.160 に答える
0

これは私の昼休みでした:)

たとえば、私が使用しているので をMAXREAD使用している場合、メモリに必要なデータの量に設定します。あなた自身の責任でコードを使用してください。私は小さなデータセットで試しました:)foreachyield return

あなたの使い方は次のようになります:

foreach (var row in StreamReader(FileName).SplitByChar(new char[] {'\r','\n'}))
{
  // Do something awesome! :)
}    

そして、次のような拡張メソッド:

public static class FileStreamExtensions
{
    public static IEnumerable<string> SplitByChar(this StreamReader stream, char[] splitter)
    {
        int MAXREAD = 1024 * 1024;

        var chars = new List<char>(MAXREAD);

        var bytes = new char[MAXREAD];
        var lastStop = 0;
        var read = 0;

        while (!stream.EndOfStream)
        {
            read = stream.Read(bytes, 0, MAXREAD);
            lastStop = 0;

            for (int i = 0; i < read; i++)
            {
                if (bytes[i] == splitter[0])
                {
                    var assume = true;
                    for (int p = 1; p < splitter.Length; p++)
                    {
                        assume &= splitter[p] == bytes[i + p];
                    }

                    if (assume)
                    {
                        chars.AddRange(bytes.Skip(lastStop).Take(i - lastStop));

                        var res = new String(chars.ToArray());
                        chars.Clear();
                        yield return res;

                        i += splitter.Length - 1;
                        lastStop = i + 1;
                    }
                }
            }
            chars.AddRange(bytes.Skip(lastStop));
        }

        chars.AddRange(bytes.Skip(lastStop).Take(read - lastStop));
        yield return new String(chars.ToArray());
    }
}
于 2012-10-26T12:21:13.640 に答える
0

StreamReader を使用して、ファイルを 1 行ずつ読み取ります。

using (StreamReader sr = new StreamReader(filePath))
{
  while (true)
  {
    string line = sr.ReadLine();
    if (line == null)
      break;
  }
}
于 2012-10-26T11:25:19.043 に答える
0

どうですか

        StreamReader sr = new StreamReader(path);
        while (!sr.EndOfStream)
        {
                string line = sr.ReadLine();
        }

ストリーム リーダー アプローチを使用すると、ファイル全体がメモリに読み込まれないことを意味します。

于 2012-10-26T11:29:12.437 に答える