草庐IT

c# - 处理非常大的XML文件

coder 2024-07-01 原文

我需要处理具有以下结构的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#程序处理文件
  • 甚至在我开始之前,我就知道这是行不通的,因为我敢肯定每个人都会同意,但是我还是尝试了一下,然后在具有30GB内存的VM(因为我的笔记本电脑只有4GB RAM)上运行了该应用程序。该应用程序最终使用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++的知识,然后可能还不太高效。

  • 我以为我会先寻求一些建议,然后再去做,可能会浪费我的时间。

    在此先感谢您的时间和协助。

    编辑

    因此,在我开始处理文件之前,请先检查文件的大小并检查其大小,以期向用户提供有关处理可能需要多长时间的反馈。我对计算进行了截屏:

    大约是每秒1500行。如果平均行长约为50个字符,则每行50个字节,即每秒75 KB,对于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分钟::(

    最佳答案

    您可以将XML阅读为元素的逻辑流,而不必尝试逐行阅读并自己重新整理。在end of this article上查看代码示例

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

    关于c# - 处理非常大的XML文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20488932/

    有关c# - 处理非常大的XML文件的更多相关文章

    1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    2. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

      我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

    4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    5. ruby - 将差异补丁应用于字符串/文件 - 2

      对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

    6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

      我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

    7. ruby - 如何指定 Rack 处理程序 - 2

      Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

    8. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

      使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

    9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

      我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

    10. Ruby 写入和读取对象到文件 - 2

      好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

    随机推荐