2009-08-19 61 views
4

我想从一个对象(包含嵌套集合)生成大量数据的XML文件。 但是XML有一个限制,它不能超过50MBC#中的文件大小限制或限制

有没有什么好的方法可以做到这一点?

更新:速度并不重要,主要的是分裂成50MB为每个文件

+0

你打算怎么处理剩下的事情?你的输出文件会是什么样子? – 2012-09-19 23:58:45

+0

将它分成50MB文件有什么问题? – 2012-09-20 17:44:02

回答

1

你有没有考虑书面方式就像一个字符串的XML文件,而不是使用.NET中的XML支持。

我正在向XML写入大约10GB的数据,因为这是工具可以使用它的唯一方式。

我有这样一个问题,但我的XML是如此简单,我只是用一个TextWriter和嵌套的for循环编写XML。

工作了一个魅力,加上比XML对象快得多。

+0

一切都比xml对象更快;) – NotMe 2009-08-19 02:45:55

2

你可以写很大的XML文件的XmlWriter的XDocument没有任何问题。

这里是一个示例。本示例在不到5秒的时间内生成一个63MB的xml文件。对于这个例子,我使用类XmlWriter

using (XmlWriter writer = XmlWriter.Create("YourFilePath")) 
{ 
    writer.WriteStartDocument(); 

    writer.WriteStartElement("Root"); 

    for (int i = 0; i < 1000000; i++) //Write one million nodes. 
    { 
     writer.WriteStartElement("Root"); 
     writer.WriteAttributeString("value", "Value #" + i.ToString()); 
     writer.WriteString("Inner Text #" + i.ToString()); 
     writer.WriteEndElement(); 
    } 
    writer.WriteEndElement(); 

    writer.WriteEndDocument(); 
} 
+0

我已经使用这种方法写入/读取了多个千兆字节的xml文件,它工作正常。对于额外的功劳,您可以通过GzipStream将其连接起来以压缩文件... – 2009-08-19 03:19:09

2

在我的工作中遇到类似的要求。我的最大努力(直观,易于实施,相对高性能)如下。我基本上写了一个XmlWriter,监视基础流。当它超过我的文件大小限制时,我完成当前的Xml片段,保存文件,关闭流。

然后在第二次通过,我加载完整DOM到存储器中,并反复地删除节点和保存文档,直到它是可以接受的尺寸。

例如

// arbitrary limit of 10MB 
long FileSizeLimit = 10*1024*1024; 

// open file stream to monitor file size 
using (FileStream file = new FileStream("some.data.xml", FileMode.Create)) 
using (XmlWriter writer = XmlWriter.Create(file)) 
{ 
    writer.WriteStartElement("root"); 

    // while not greater than FileSizeLimit 
    for (; file.Length < FileSizeLimit;) 
    { 
     // write contents 
     writer.WriteElementString(
      "data", 
      string.Format("{0}/{0}/{0}/{0}/{0}", Guid.NewGuid())); 
    } 

    // complete fragment; this is the trickiest part, 
    // since a complex document may have an arbitrarily 
    // long tail, and cannot be known during file size 
    // sampling above 
    writer.WriteEndElement(); 
    writer.Flush(); 
} 

// iteratively reduce document size 
// NOTE: XDocument will load full DOM into memory 
XDocument document = XDocument.Load("some.data.xml"); 
XElement root = document.Element("root"); 
for (; new FileInfo("some.data.xml").Length > FileSizeLimit;) 
{ 
    root.LastNode.Remove(); 
    document.Save("some.data.xml"); 
} 

有办法来改善这一点;如果内存是一个约束条件,一种可能性是重写迭代位以获取第一遍中实际写入的节点的数量,然后将文件重新写入少一个元素,然后继续写入,直到完整文档具有所需的大小。

这最后的建议可能是要走的路线,尤其是如果您已经需要跟踪写入的元素以恢复在另一个文件中写入。

希望这会有所帮助!


编辑

虽然直观,更容易实现,我觉得这值得研究上面提到的优化。这就是我得到的。

一种扩展方法,有助于写入祖先节点(即容器的节点,而所有其他种类的标记),

// performs a shallow copy of a given node. courtesy of Mark Fussell 
// http://blogs.msdn.com/b/mfussell/archive/2005/02/12/371546.aspx 
public static void WriteShallowNode(this XmlWriter writer, XmlReader reader) 
{ 

    switch (reader.NodeType) 
    { 
     case XmlNodeType.Element: 
      writer.WriteStartElement(
       reader.Prefix, 
       reader.LocalName, 
       reader.NamespaceURI); 
      writer.WriteAttributes(reader, true); 
      if (reader.IsEmptyElement) 
      { 
       writer.WriteEndElement(); 
      } 
      break; 
     case XmlNodeType.Text: writer.WriteString(reader.Value); break; 
     case XmlNodeType.Whitespace: 
     case XmlNodeType.SignificantWhitespace: 
      writer.WriteWhitespace(reader.Value); 
      break; 
     case XmlNodeType.CDATA: writer.WriteCData(reader.Value); break; 
     case XmlNodeType.EntityReference: 
      writer.WriteEntityRef(reader.Name); 
      break; 
     case XmlNodeType.XmlDeclaration: 
     case XmlNodeType.ProcessingInstruction: 
      writer.WriteProcessingInstruction(reader.Name, reader.Value); 
      break; 
     case XmlNodeType.DocumentType: 
      writer.WriteDocType(
       reader.Name, 
       reader.GetAttribute("PUBLIC"), 
       reader.GetAttribute("SYSTEM"), 
       reader.Value); 
      break; 
     case XmlNodeType.Comment: writer.WriteComment(reader.Value); break; 
     case XmlNodeType.EndElement: writer.WriteFullEndElement(); break; 
    } 
} 

和将执行剪裁(不是一个扩展方法中,由于延伸中的任一种方法参数类型会有点模糊)。

// trims xml file to specified file size. does so by 
// counting number of "victim candidates" and then iteratively 
// trimming these candidates one at a time until resultant 
// file size is just less than desired limit. does not 
// consider nested victim candidates. 
public static void TrimXmlFile(string filename, long size, string trimNodeName) 
{ 
    long fileSize = new FileInfo(filename).Length; 
    long workNodeCount = 0; 

    // count number of victim elements in xml 
    if (fileSize > size) 
    { 
     XmlReader countReader = XmlReader.Create(filename); 
     for (; countReader.Read();) 
     { 
      if (countReader.NodeType == XmlNodeType.Element && 
       countReader.Name == trimNodeName) 
      { 
       workNodeCount++; 
       countReader.Skip(); 
      } 
     } 
     countReader.Close(); 
    } 

    // if greater than desired file size, and there is at least 
    // one victim candidate 
    string workFilename = filename+".work"; 
    for (; 
     fileSize > size && workNodeCount > 0; 
     fileSize = new FileInfo(filename).Length) 
    { 
     workNodeCount--; 
     using (FileStream readFile = new FileStream(filename, FileMode.Open)) 
     using (FileStream writeFile = new FileStream(
      workFilename, 
      FileMode.Create)) 
     { 
      XmlReader reader = XmlReader.Create(readFile); 
      XmlWriter writer = XmlWriter.Create(writeFile); 

      long j = 0; 
      bool hasAlreadyRead = false; 
      for (; (hasAlreadyRead) || reader.Read();) 
      { 

       // if node is a victim node 
       if (reader.NodeType == XmlNodeType.Element && 
        reader.Name == trimNodeName) 
       { 
        // if we have not surpassed this iteration's 
        // allowance, preserve node 
        if (j < workNodeCount) 
        { 
         writer.WriteNode(reader, true); 
        } 
        j++; 

        // if we have exceeded this iteration's 
        // allowance, trim node (and whitespace) 
        if (j >= workNodeCount) 
        { 
         reader.ReadToNextSibling(trimNodeName); 
        } 
        hasAlreadyRead = true; 
       } 
       else 
       { 
        // some other xml content we should preserve 
        writer.WriteShallowNode(reader); 
        hasAlreadyRead = false; 
       } 
      } 
      writer.Flush(); 
     } 
     File.Copy(workFilename, filename, true); 
    } 
    File.Delete(workFilename); 
} 

如果你的XML包含空格的格式,最后剩下的牺牲节点和关闭容器元素标记之间的任何空白都将丢失。这可以通过更改skip子句(移动j++语句后跳过)来缓解,但最终会产生额外的空白。上面介绍的解决方案生成了源文件的最小文件大小副本。