极大的单行文件解析

时间:2014-10-08 01:39:06

标签: c# string file-io

我正在从网站下载数据,网站会在非常大块中向我提供数据。在非常大的块中,我需要单独解析“块”。这些“块”以“(ClinicalData)”开头,以“(/ ClinicalData)”结尾。因此,示例字符串看起来像:

(ClinicalData)(ID="1")(/ClinicalData)(ClinicalData)(ID="2")(/ClinicalData)(ClinicalData)(ID="3")(/ClinicalData)(ClinicalData)(ID="4")(/ClinicalData)(ClinicalData)(ID="5")(/ClinicalData)

在“理想”情况下,该块意味着是单行数据,但有时会出现错误的换行符。由于我想解析块中的(ClinicalData)块,我想逐行解析数据。因此,我取文本文件,将其全部读入StringBuilder,删除新行(以防万一),然后插入我自己的换行符,这样我就可以逐行读取。

StringBuilder dataToWrite = new StringBuilder(File.ReadAllText(filepath), Int32.MaxValue);

// Need to clear newline characters just in case they exist.
dataToWrite.Replace("\n", "");

// set my own newline characters so the data becomes parse-able by line 
dataToWrite.Replace("<ClinicalData", "\n<ClinicalData");

// set the data back into a file, which is then used in a StreamReader to parse by lines.
File.WriteAllText(filepath, dataToWrite.ToString());

这一直很好(虽然可能效率不高,但至少它对我很友好:)),直到我没有遇到一大块数据作为280MB大文件提供给我。

现在我收到一个带有这个块的System.OutOfMemoryException,我只是无法找到解决方法。我相信问题是StringBuilder无法处理280MB的直文?好吧,我已经尝试了字符串拆分,regex.match拆分,以及各种其他方法将其分解为保证“(ClinicalData)块”,但我继续得到内存异常。我也没有运气试图读取预定义块(例如:使用.ReadBytes)。

关于如何处理280MB大型,可能但实际上不是单行文本的任何建议都会很棒!

2 个答案:

答案 0 :(得分:1)

这是一种非常低效的阅读文本文件的方式,更不用说大文本了。如果您只需要一次传递,替换或添加单个字符,则应使用StreamReader。如果你只需要一个前瞻字符,你只需要保持一个中间状态,如:

enum ReadState
{
    Start,
    SawOpen
}


using (var sr = new StreamReader(@"path\to\clinic.txt"))
using (var sw = new StreamWriter(@"path\to\output.txt"))
{
    var rs = ReadState.Start;
    while (true)
    {
        var r = sr.Read();
        if (r < 0)
        {
            if (rs == ReadState.SawOpen)
                sw.Write('<');
            break;
        }

        char c = (char) r;
        if ((c == '\r') || (c == '\n'))
            continue;

        if (rs == ReadState.SawOpen)
        {
            if (c == 'C')
                sw.WriteLine();

            sw.Write('<');
            rs = ReadState.Start;
        }

        if (c == '<')
        {
            rs = ReadState.SawOpen;
            continue;
        }

        sw.Write(c);
    }
}

答案 1 :(得分:0)

首先,我不认为您需要将所有文本放在StringBuilder中,因为您甚至不能将部分连接到它。您可以尝试以下方法:

File.ReadAllText(filepath).Replace("\n", "").Replace("<ClinicalData", "\n<ClinicalData");

为什么不尝试使用StreamReader执行此任务?你可以选择一个&#34; chunk&#34;要读取的大小,然后将这些块拆分为(ClinicalData)数据(/ ClinicalData)部分。以下是有关如何执行此操作的详细代码:

        char[] buffer = new char[1024];
        string remainder = string.Empty;
        List<ClientData> list = new List<ClientData>();

        using (StreamReader reader = File.OpenText(@"source.txt"))
        {
            while (reader.Read(buffer, 0, 1024) > 0)
            {
                remainder = Parse(remainder + new string(buffer), list);
            }
        }

使用以下方法:

string Parse(string value, List<ClientData> list)
{
    string[] parts = value.Split(new string[1] { "</ClientData>" }, StringSplitOptions.None);
    for (int i = 0; i < parts.Length - 1; i++)
        list.Add(new ClientData(parts[i]));

    return parts[parts.Length - 1];
}

和ClientData类,但是你实现了它:

class ClientData
{
    public ClientData(string value)
    {
        // fill in however you are already parsing out ID, and other info
    }
}

有很多方法可以实现这样的功能,但希望这可以帮助您入门。

StreamReader的ReadLine()方法只是您可以从文件中读取文本的众多方法之一。您可以读入具有指定长度的缓冲区,然后解析出ClinicalData标记。如果你愿意,我可以提供一个例子。 http://msdn.microsoft.com/en-us/library/9kstw824%28v=vs.110%29.aspx

或者,如果您正在读取XML文件,则XmlReader是另一种选择。 http://msdn.microsoft.com/en-us/library/system.xml.xmlreader%28v=vs.110%29.aspx