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

标签 c#

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

示例数据:
1'~'2'~'3#####11'~'12'~'13

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

我不断在下一行得到 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");

        }
    }
}

最佳答案

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

使用 StreamReader 我们可以从源流(以您需要的任何编码)读取一系列字符到一个数组中。使用它和 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();
}

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

为了将您的文件从您描述的输入格式转换为简单的制表符分隔格式,您可以按照以下几行做一些事情:

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"));
    }
}

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

关于c# - 使用自定义分隔符解析一个巨大的文本文件(大约 2GB),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47804688/

相关文章:

c# - 如果存在验证错误,也要更新 ViewModel 中的属性

c# - 统一计时器问题

c# - VS 201 0's Productivity Power Tools' s 右侧的选项卡井

c# - IQuery NHibernate - 它必须是一个列表吗?

c# - YouTube C# .NET API : Uploading video and getting events when finished

c# - .NET 编码与字符集的关系

c# - 如何在 C# 中用索引值替换文本

c# - 以下代码如何自动线程安全?

c# - 发布时的 TransformXml Web.config

c# - 从 ObservableCollection 项目中删除名称出现在其他列表中的项目