处理非常大的XML文件

时间:2013-12-10 07:57:12

标签: c# c++ xml large-files

我需要使用以下结构处理XML文件:

<FolderSizes>
    <Version></Version>
    <DateTime Un=""></DateTime>
    <Summary>
        <TotalSize Bytes=""></TotalSize>
        <TotalAllocated Bytes=""></TotalAllocated>
        <TotalAvgFileSize Bytes=""></TotalAvgFileSize>
        <TotalFolders Un=""></TotalFolders>
        <TotalFiles Un=""></TotalFiles>
    </Summary>
    <DiskSpaceInfo>
        <Drive Type="" Total="" TotalBytes="" Free="" FreeBytes="" Used=""
               UsedBytes=""><![CDATA[ ]]></Drive>
    </DiskSpaceInfo>
    <Folder ScanState="">
        <FullPath Name=""><![CDATA[ ]]></FullPath>
        <Attribs Int=""></Attribs>
        <Size Bytes=""></Size>
        <Allocated Bytes=""></Allocated>
        <AvgFileSz Bytes=""></AvgFileSz>
        <Folders Un=""></Folders>
        <Files Un=""></Files>
        <Depth Un=""></Depth>
        <Created Un=""></Created>
        <Accessed Un=""></Accessed>
        <LastMod Un=""></LastMod>
        <CreatedCalc Un=""></CreatedCalc>
        <AccessedCalc Un=""></AccessedCalc>
        <LastModCalc Un=""></LastModCalc>
        <Perc><![CDATA[ ]]></Perc>
        <Owner><![CDATA[ ]]></Owner>

        <!-- Special element; see paragraph below -->
        <Folder></Folder>
    </Folder>
</FolderSizes>

<Folder>元素的特殊之处在于它在<FolderSizes>元素中重复,但也可以在其自身内出现;我估计大约有5个级别。

问题是文件真的很大,高达11GB,所以我很难处理它 - 我有XML文档的经验,但没有任何规模。

我想要做的是将信息导入SQL数据库,因为这样我就能以任何必要的方式处理信息,而不必关心这个巨大的,不切实际的文件。

以下是我尝试过的事情:

  • 只需加载文件并尝试使用XmlDocument或XDocument对象使用简单的C#程序对其进行处理
    • 在我开始之前,我知道这不起作用,因为我相信每个人都会同意,但无论如何我都尝试过,并在VM上运行应用程序(因为我的笔记本只有4GB内存),内存为30GB。该应用程序最终使用24GB内存,并且耗时非常长,所以我刚刚取消了它。
  • 尝试使用XmlReader对象处理文件
    • 这种方法效果更好,因为它没有使用尽可能多的内存,但我仍然遇到了一些问题:
      • 这花了很长时间,因为我一次只读一行文件。
      • 一次处理一行文件使得很难真正处理XML中包含的数据,因为现在你必须检测标记的开头,然后检测标记的结尾(希望如此),然后创建来自该信息的文档,读取信息,尝试确定它属于哪个父标记,因为我们有多个级别...声音容易出现问题和错误
      • 我提到它需要花费很长时间才能一行读取一行文件;并且仍然没有实际处理该行 - 只是阅读它。
  • 使用SQL Server导入信息
    • 我使用XQuery创建了一个存储过程,并在其自身内递归运行处理<Folder>元素。这很顺利 - 我认为比其他两种方法更好 - 直到其中一个<Folder>元素变得相当大,产生An XML operation resulted an XML data type exceeding 2GB in size. Operation aborted.错误。我读到了它,我不认为这是一个可调节的极限。

以下是我认为应该尝试的更多内容:

  • 重写我的C#应用​​程序以使用非托管代码
    • 我对非托管代码没有多少经验,所以我不确定它的工作情况以及如何使其尽可能不受管理。
    • 我曾经写过一个小应用程序,可以使用我的网络摄像头,接收图像,反转颜色,然后将其绘制到面板上。使用普通的托管代码不起作用 - 结果大约是每秒2帧。重写颜色反转方法使用非托管代码解决了问题。这就是为什么我认为不受管理可能是一个解决方案。
  • 而不是C ++而不是C#
    • 不确定这是否真的是一个解决方案。 C#一定会更好吗?比不受管理的C#好吗?
    • 这里的问题是我之前没有真正使用过C ++,所以在开始使用C ++之前我需要先了解一些关于C ++的知识,然后可能还不是很有效。< / LI>

在我走得更远之前,我想我会先征求一些意见,可能会浪费我的时间。

提前感谢您的时间和帮助。

修改

因此,在我开始处理文件之前,我会检查它并检查大小,以便向用户提供有关处理可能需要多长时间的反馈;我制作了计算的截图:

18 minutes in; 1.67mil lines

这大约是每秒1500行;如果我的数学是正确的,如果平均行长度大约为50个字符,即每行50个字节,即每秒75千字节,对于11GB文件应该需要大约40个小时。但这只是踩到每一行。它实际上并不处理该行或对其进行任何操作,因此当开始时,处理速率会显着下降。

这是在大小计算期间运行的方法:

    private int _totalLines = 0;
    private bool _cancel = false; // set to true when the cancel button is clicked

    private void CalculateFileSize()
    {
        xmlStream = new StreamReader(_filePath);
        xmlReader = new XmlTextReader(xmlStream);

        while (xmlReader.Read())
        {
            if (_cancel)
                return;

            if (xmlReader.LineNumber > _totalLines)
                _totalLines = xmlReader.LineNumber;

            InterThreadHelper.ChangeText(
                lblLinesRemaining, 
                string.Format("{0} lines", _totalLines));

            string elapsed = string.Format(
                "{0}:{1}:{2}:{3}",
                timer.Elapsed.Days.ToString().PadLeft(2, '0'),
                timer.Elapsed.Hours.ToString().PadLeft(2, '0'),
                timer.Elapsed.Minutes.ToString().PadLeft(2, '0'),
                timer.Elapsed.Seconds.ToString().PadLeft(2, '0'));

            InterThreadHelper.ChangeText(lblElapsed, elapsed);

            if (_cancel)
                return;
        }

        xmlStream.Dispose();
    }

仍在奔跑,27分钟:(

1 个答案:

答案 0 :(得分:2)

您可以将XML作为元素的逻辑流读取,而不是尝试逐行读取并将其重新组合在一起。请参阅end of this article

上的代码示例

另外,您的问题已经被问到here