使用自定义分隔符解析一个巨大的文本文件(大约 2GB)

Liz*_*zzy 6 c#

我有一个大约 2GB 的巨大文本文件,我试图用 C# 解析它。该文件具有用于行和列的自定义分隔符。我想解析文件并提取数据并通过插入列标题并用换行符替换 RowDelimiter 和按制表符替换 ColumnDelimiter 来将数据写入另一个文件,以便我可以以表格格式获取数据。

样本数据:
1'~'2'~'3#####11'~'12'~'13

行分隔符: 列 #####
分隔符: '~'

我继续在System.OutOfMemoryException下面的线路上

while ((line = rdr.ReadLine()) != null)

public void ParseFile(string inputfile,string outputfile,string header)
{

    using (StreamReader rdr = new StreamReader(inputfile))
    {
        string line;

        while ((line = rdr.ReadLine()) != null)
        {
            using (StreamWriter sw = new StreamWriter(outputfile))
            {
                //Write the Header row
                sw.Write(header);

                //parse the file
                string[] rows = line.Split(new string[] { ParserConstants.RowSeparator },
                    StringSplitOptions.None);

                foreach (string row in rows)
                {
                    string[] columns = row.Split(new string[] {ParserConstants.ColumnSeparator},
                        StringSplitOptions.None);
                    foreach (string column in columns)
                    {
                        sw.Write(column + "\\t");
                    }
                    sw.Write(ParserConstants.NewlineCharacter);
                    Console.WriteLine();
                }
            }

            Console.WriteLine("File Parsing completed");

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Cor*_*rey 2

正如评论中已经提到的,您将无法使用它ReadLine来处理此问题,您必须一次处理一个字节或一个字符的数据。好消息是,无论如何,这基本上是ReadLine有效的,所以在这种情况下我们不会损失太多。

使用 aStreamReader我们可以将源流中的一系列字符(以您需要的任何编码)读取到数组中。使用它和 a,StringBuilder我们可以按块处理流并检查途中的分隔符序列。

这是一个处理任意分隔符的方法:

public static IEnumerable<string> ReadDelimitedRows(StreamReader reader, string delimiter)
{
    char[] delimChars = delimiter.ToArray();
    int matchCount = 0;
    char[] buffer = new char[512];
    int rc = 0;
    StringBuilder sb = new StringBuilder();

    while ((rc = reader.Read(buffer, 0, buffer.Length)) > 0)
    {
        for (int i = 0; i < rc; i++)
        {
            char c = buffer[i];
            if (c == delimChars[matchCount])
            {
                if (++matchCount >= delimChars.Length)
                {
                    // found full row delimiter
                    yield return sb.ToString();
                    sb.Clear();
                    matchCount = 0;
                }
            }
            else
            {
                if (matchCount > 0)
                {
                    // append previously matched portion of the delimiter
                    sb.Append(delimChars.Take(matchCount));
                    matchCount = 0;
                }
                sb.Append(c);
            }
        }
    }
    // return the last row if found
    if (sb.Length > 0)
        yield return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)

这应该可以处理部分块分隔符出现在实际数据中的任何情况。

为了将文件从您描述的输入格式转换为简单的制表符分隔格式,您可以执行以下操作:

const string RowDelimiter = "#####";
const string ColumnDelimiter = "'~'";

using (var reader = new StreamReader(inputFilename))
using (var writer = new StreamWriter(File.Create(ouputFilename)))
{
    foreach (var row in ReadDelimitedRows(reader, RowDelimiter))
    {
        writer.Write(row.Replace(ColumnDelimiter, "\t"));
    }
}
Run Code Online (Sandbox Code Playgroud)

这应该会处理得相当快,而不会占用太多内存。对于非 ASCII 输出可能需要进行一些调整。