在C#中使用非常庞大的XML文件

时间:2010-07-26 18:49:30

标签: c# xml

我有这个非常庞大的大小为2.8GB的XML文件。这是波兰维基百科的文章转储。这个文件的大小对我来说很成问题。任务是搜索此文件以获取大量数据。我所拥有的只是文章的标题。我以为我可以对这些标题进行排序并在文件中使用一个线性循环。想法并不是那么糟糕,但文章按字母顺序排序。它们按ID排序,我不知道先验

所以,我的第二个想法是制作该文件的索引。要以下列格式存储在其他文件(或数据库)行中:title;id;index(可能没有ID)。我的另一个问题是我向你寻求帮助。假设是,如果我有所需标签的索引,我可以使用简单的Seek方法在文件中移动光标而不读取所有内容等。对于较小的文件,我认为这可以正常工作。但在我的电脑(笔记本电脑,C2D proc,Win7,VS2008)上我收到应用程序没有响应的错误。

在我的程序中,我正在从文件中读取每一行并检查它是否包含我需要的标记。我也在计算我读取的所有字节并以上述格式保存行。因此,虽然索引程序被挂断了。但到那时结果索引文件为36.2MB,最后一个索引为2,872,765,202(B),而整个XML文件为3,085,439,630 B长。

我的第三个想法是将文件分成更小的部分。准确地说是26件(拉丁语中有26个字母),每个字母仅包含以相同字母开头的条目,例如在a.xml中,标题以“A”字母开头的所有条目。最终文件将像数十MB,我认为最大约为200 MB。但是阅读整个文件存在同样的问题。

要阅读我最常用的文件:使用StreamReader。我在某处读到StreamReader和来自XmlReader的{​​{1}}类是最快的方法。 System.Xml StreamReader甚至更快XmlReader。很明显,我无法将所有这些文件加载​​到内存中。我已经安装了3GB的RAM,Win7在满载时需要800MB-1GB。

所以我在寻求帮助。什么是最好的。关键是搜索此XML文件必须快速。必须更快,然后以HTML格式下载单个维基百科页面。我甚至不确定这是否可能。

也许将所有需要的内容加载到数据库中?也许那会更快?但我仍然需要至少阅读整个文件一次。

我不确定1个问题长度是否存在一些限制,但我还会在此处提供我的索引源代码示例。

while (reading)
{
    if (!reader.EndOfStream)
    {
        line = reader.ReadLine();
        fileIndex += enc.GetByteCount(line) + 2; //+2 - to cover characters \r\n not included into line
        position = 0;
    }
    else
    {
        reading = false;
        continue;
    }

    if (currentArea == Area.nothing)    //nothing interesting at the moment
    {
         //search for position of <title> tag
         position = MoveAfter("&lt;title>", line, position);    //searches until it finds &lt;title> tag
         if (position >= 0) currentArea = Area.title;
         else continue;
    }

    (...)

    if (currentArea == Area.text)
    {
         position = MoveAfter("&lt;text", line, position);
         if (position >= 0)
         {
              long index = fileIndex;
              index -= line.Length;
              WriteIndex(currentTitle, currentId, index);
              currentArea = Area.nothing;
         }
         else continue;
     }
 }

 reader.Close();
 reader.Dispose();
 writer.Close();
 }

 private void WriteIndex(string title, string id, long index)
 {
     writer.WriteLine(title + ";" + id + ";" + index.ToString());
 }

最诚挚的问候和提前谢谢,

文图斯

编辑:链接到此Wiki的转储http://download.wikimedia.org/plwiki/20100629/

10 个答案:

答案 0 :(得分:7)

嗯....如果您要搜索它,我强烈建议您找到一种比处理文件本身更好的方法。我建议你提到将它放入一个规范化和索引编制的数据库中并在那里进行搜索。你做的任何其他事情都将有效地复制数据库的功能。

但是,这样做需要时间。 XmlTextReader可能是你最好的选择,它一次只能运行一个节点。 LINQ to XML也应该是一个相当有效的处理,但我没有尝试过大文件,因此无法评论。

请问:这个巨大的XML文件来自哪里?也许有一种方法可以在源头处理这种情况,而不是在处理3 GB文件之前。

答案 1 :(得分:6)

好吧,如果它符合您的要求,我首先会将此XML导入到SQL Server之类的RDMS中,然后针对此SQL Server进行查询。

使用正确的索引(全文索引,如果你想搜索大量文本),它应该非常快......

它将减少由库解析XML文件带来的大量开销......

答案 2 :(得分:2)

我喜欢创建索引的想法 - 您可以保持代码超级简单,并且不需要任何可怕的依赖项,例如数据库:)

所以 - 创建一个存储以下内容的索引

[content to search]:[byte offset to the start of the xml node that contains the content]

要捕获字节偏移量,您需要在输入文件上创建自己的流,并从中创建一个读取器。你将查询每个读者的位置。阅读(..)。一个示例索引记录是:

"Now is the winter of our discontent":554353

这意味着xml文件中包含“现在是我们不满的冬天”的条目位于字节位置554,353处的节点处。注意:我很想对索引的搜索部分进行编码,这样就不会与你使用的分隔符发生碰撞。

要读取索引,请扫描磁盘上的索引(即不要将其加载到内存中),以查找相应的记录。找到后,您将获得字节偏移量。现在在.xml文件上创建一个新的Stream并将其位置设置为字节偏移量 - 创建一个新的阅读器并从该点读取文档。

答案 3 :(得分:1)

您可以将文件存储在couchDB中。我写了一个python脚本来做到这一点:

import couchdb
import datetime
import time
from lxml import etree

couch = couchdb.Server()
db = couch["wiki"]

infile = '/Users/johndotnet/Downloads/plwiki-20100629-pages-articles.xml'


context = etree.iterparse(source=infile, events=("end", ), tag='{http://www.mediawiki.org/xml/export-0.4/}page')


for event, elem in context:
    #dump(elem)
 couchEle = {}
 for ele in elem.getchildren():
  if ele.tag == "{http://www.mediawiki.org/xml/export-0.4/}id":
   couchEle['id'] = ele.text
  if ele.tag == "{http://www.mediawiki.org/xml/export-0.4/}title":
   couchEle['title'] = ele.text
  if ele.tag == "{http://www.mediawiki.org/xml/export-0.4/}revision":
   for subEle in ele.getchildren():
    if subEle.tag == "{http://www.mediawiki.org/xml/export-0.4/}text":
     couchEle['text'] = subEle.text


 db[couchEle['title']] = couchEle

这应该将包含id,title和text的所有文章导入couchDB。

现在你应该这样做一个查询:

code = '''
  function(doc) { 
   if(doc.title.indexOf("Brzeg") > -1) {
    emit(doc._id, doc);
   }

  }
  '''
results = db.query(code)

希望它有所帮助!

答案 4 :(得分:0)

XmlReader会很快,但您需要验证它在您的方案中是否足够快。假设我们正在寻找位于名为Item的节点中的值:

using (var reader = XmlReader.Create("data.xml"))
{
    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "Item")
        {
            string value = reader.ReadElementContentAsString();
            if (value == "ValueToFind")
            {
                // value found
                break;
            }
        }
    }
}

答案 5 :(得分:0)

我会这样做:

1)将XML分解为更小的文件。例如,如果XML看起来像这样,那么我将为每个文章节点创建一个文件,其名称与title属性匹配。如果标题不是唯一的,那么我只会对文件进行编号。

由于这是很多文件,我会将它们分成子目录,每个目录不超过1,000个文件。

<root>
    <article title="aaa"> ... </article>
    <article title="bbb"> ... </article>
    <article title="ccc"> ... </article>
</root>

2)创建一个索引表,其中包含您要搜索的文件名和列。

3)作为选项,您可以将XML片段存储在数据库中而不是存储在硬盘驱动器上。 SQL Server的varChar(MAX)类型对此有利。

答案 6 :(得分:0)

将其转储到Solr索引中并使用它来搜索它。您可以将Solr作为独立的搜索引擎运行,并使用一小段脚本来循环遍历文件并将每篇文章转储到索引中。 Solr然后为您决定索引的任何字段提供全文搜索...

答案 7 :(得分:0)

您可以快速搜索此内容的唯一方法是将其存储在数据库中,就像其他人建议的那样。如果数据库不是一个选项,那么它将花费很长时间,毫无疑问。我要做的是创建一个多线程应用程序。创建将读入数据的工作线程,并将其粘贴到字符串队列中。有5个线程通过整个文件进行分段(所以一个线程将从头开始,第二个线程将开始进入文件的1/5,第三个线程将开始2/5的方式等等)。同时,让另一个线程读取字符串队列并搜索您要查找的内容。然后让线程在完成后出列。这需要一段时间,但它不应该崩溃或消耗大量的内存。

如果您发现它占用了大量内存,则设置队列可以容纳的项目数限制,并让线程暂停,直到队列大小低于此阈值。

答案 8 :(得分:0)

您可以在SQL Server中使用XML DataType,它支持最多2GB的xml数据。 您可以使用它直接查询xml。

参考这个。 http://technet.microsoft.com/en-us/library/ms189887(v=sql.105).aspx

希望这有帮助!

答案 9 :(得分:0)

我知道这个问题//答案很老。但我最近一直在解决这个问题,并且发现我会使用JSON.Net(newtonking)。这就像将XML文档结果反序列化为C#对象一样简单。

现在,我的文档(结果)只有几MB的大小(目前平均为5MB),但我可以看到随着Solr索引的增长。就目前而言,我的结果很快。

关于CodePlex的讨论,参考表演。