如何将 XML 文档分成三份(或者,甚至更好,分成 n 份)?

Posted

技术标签:

【中文标题】如何将 XML 文档分成三份(或者,甚至更好,分成 n 份)?【英文标题】:How can I split an XML document into thirds (or, even better, n pieces)? 【发布时间】:2010-09-05 16:33:42 【问题描述】:

我想使用我熟悉的语言 - Java、C#、Ruby、php、C/C++,尽管任何语言或伪代码的示例都非常受欢迎。

将大型 XML 文档拆分为仍然是有效 XML 的较小部分的最佳方法是什么?出于我的目的,我需要将它们分成大约三分之一或四分之一,但为了提供示例,将它们分成 n 个组件会很好。

【问题讨论】:

【参考方案1】:

使用 DOM 解析 XML 文档无法扩展。

这个Groovy-脚本使用StAX(XML 流API)在***元素(与根文档的第一个子元素共享相同的QName)之间拆分XML 文档。它非常快,可以处理任意大型文档,并且在您想要将大型批处理文件拆分为较小的部分时非常有用。

需要 Java 6 上的 Groovy 或 StAX API 和实现,例如 CLASSPATH 中的 Woodstox

import javax.xml.stream.*

pieces = 5
input = "input.xml"
output = "output_%04d.xml"
eventFactory = XMLEventFactory.newInstance()
fileNumber = elementCount = 0

def createEventReader() 
    reader = XMLInputFactory.newInstance().createXMLEventReader(new FileInputStream(input))
    start = reader.next()
    root = reader.nextTag()
    firstChild = reader.nextTag()
    return reader


def createNextEventWriter () 
    println "Writing to '$filename = String.format(output, ++fileNumber)'"
    writer = XMLOutputFactory.newInstance().createXMLEventWriter(new FileOutputStream(filename), start.characterEncodingScheme)
    writer.add(start)
    writer.add(root)
    return writer


elements = createEventReader().findAll  it.startElement && it.name == firstChild.name .size()
println "Splitting $elements <$firstChild.name.localPart> elements into $pieces pieces"
chunkSize = elements / pieces
writer = createNextEventWriter()
writer.add(firstChild)
createEventReader().each  
    if (it.startElement && it.name == firstChild.name) 
        if (++elementCount > chunkSize) 
            writer.add(eventFactory.createEndDocument())
            writer.flush()
            writer = createNextEventWriter()
            elementCount = 0
        
    
    writer.add(it)

writer.flush()

【讨论】:

【参考方案2】:

当然,您总是可以提取***元素(这是否是您想要的粒度取决于您)。在 C# 中,您将使用 XmlDocument 类。例如,如果您的 XML 文件看起来像这样:

<Document>
  <Piece>
     Some text
  </Piece>
  <Piece>
     Some other text
  </Piece>
</Document>

然后你会使用这样的代码来提取所有的片段:

XmlDocument doc = new XmlDocument();
doc.Load("<path to xml file>");
XmlNodeList nl = doc.GetElementsByTagName("Piece");
foreach (XmlNode n in nl)

    // Do something with each Piece node

一旦你有了节点,你就可以在你的代码中对它们做一些事情,或者你可以将节点的整个文本转移到它自己的 XML 文档中,并对其进行操作,就好像它是一个独立的 XML 片段(包括将其保存回磁盘等)。

【讨论】:

【参考方案3】:

正如 DannySmurf 提到的,这完全是关于 xml 文档的结构。 如果您只有两个巨大的“***”标签,那么将很难将其拆分为既可以将其重新合并在一起又可以将其作为有效 xml 逐个读取的方式。 给定一个包含很多单独部分的文档,例如 DannySmurfs 示例中的文档,它应该相当容易。 伪 C# 中的一些粗略代码:

int nrOfPieces = 5;
XmlDocument xmlOriginal = some input parameter..

// construct the list we need, and fill it with XmlDocuments..
var xmlList = new List<XmlDocument>();
for (int i = 0; i < nrOfPieces ; i++)

    var xmlDoc = new XmlDocument();
    xmlDoc.ChildNodes.Add(new XmlNode(xmlOriginal.FistNode.Name));
    xmlList.Add(xmlDoc);


var nodeList = xmlOriginal.GetElementsByTagName("Piece")M
// Copy the nodes from the original into the pieces..
for (int i = 0; i < nodeList .Count; i++)

    var xmlDoc = xmlList[i % nrOfPieces];
    var nodeToCopy = nodeList[i].Clone();
    xmlDoc.FirstNode.ChildNodes.Add(nodeToCopy);

这应该会为您提供 n 个具有正确 xml 的文档,并且可以将它们重新合并在一起。 但同样,它取决于 xml 文件。

【讨论】:

【参考方案4】:

这更像是一个评论而不是一个答案,但不会:

XmlDocument doc = new XmlDocument();
doc.Load("path");

一次读取整个文件?只是认为我应该提出这一点,因为从托马斯的问题来看,他担心读取大文件并想打破这个过程..

【讨论】:

【参考方案5】:

它会一次读取整个文件。但是,根据我的经验,如果您只是在读取文件、进行一些处理(即分解它)然后继续您的工作,那么 XmlDocument 将很快完成它的创建/读取/收集周期,以至于可能没关系。

当然,这取决于“大”文件是什么。如果它是一个 30 MB 的 XML 文件(我认为它对于 XML 文件来说很大),它可能不会有任何区别。如果它是一个 500 MB 的 XML 文件,那么在没有大量 RAM 的系统上使用 XmlDocument 将变得非常有问题(但是,在这种情况下,我认为使用 XmlReader 手动选择文件的时间会更重要障碍)。

【讨论】:

【参考方案6】:

不确定您正在执行哪种类型的处理,但对于非常大的 XML,我一直是基于事件的处理的粉丝。也许这是我的 Java 背景,但我真的很喜欢 SAX。您需要进行自己的状态管理,但一旦完成,这是解析 XML 的一种非常有效的方法。

http://saxdotnet.sourceforge.net/

【讨论】:

【参考方案7】:

我将在这个上与 youphoric 一起去。对于非常大的文件,SAX(或任何其他流解析器)将在处理中提供很大帮助。使用 DOM,您可以只收集***节点,但您仍然必须解析整个文档才能做到这一点……使用流式解析器和基于事件的处理可以让您“跳过”您不感兴趣的节点;使处理速度更快。

【讨论】:

【参考方案8】:

如果您对 Perl 不是完全过敏,那么 XML::Twig 附带一个名为 xml_split 的工具,它可以拆分文档,生成格式良好的 XML 部分。您可以按树的级别、大小或 XPath 表达式进行拆分。

【讨论】:

【参考方案9】:

看起来您正在使用 C# 和 .NET 3.5。我遇到过一些帖子建议在带有 XmlReader 的文件流上使用 yield 类型的算法。

这里有几篇博文可以帮助您开始这条道路:

Streaming With Linq to SQL Part 1 Streaming With Linq To SQL Part 2

【讨论】:

【参考方案10】:

我制作了一个 YouTube 视频,显示 how to split XML files 和 foxe(来自 Firstobject 的免费 XML 编辑器)仅使用少量内存,无论输入和输出文件的大小如何。

此 CMarkup XML 读取器(拉式解析器)和 XML 写入器解决方案的内存使用量取决于从输入文件单独传输到输出文件的子文档的大小,或 16 KB 的最小块大小。

拆分()

  CMarkup xmlInput、xmlOutput;
  xmlInput.Open("50MB.xml", MDF_READFILE);
  int nObjectCount = 0, nFileCount = 0;
  而 ( xmlInput.FindElem("//ACT") )
  
    如果(nObjectCount == 0)
    
      ++n文件计数;
      xmlOutput.Open("piece" + nFileCount + ".xml", MDF_WRITEFILE );
      xmlOutput.AddElem("root");
      xmlOutput.IntoElem();
    
    xmlOutput.AddSubDoc(xmlInput.GetSubDoc());
    ++n对象计数;
    如果(nObjectCount == 5)
    
      xmlOutput.Close();
      nObjectCount = 0;
    
  
  如果(nObjectCount)
    xmlOutput.Close();
  xmlInput.Close();
  返回 nFileCount;

【讨论】:

以上是关于如何将 XML 文档分成三份(或者,甚至更好,分成 n 份)?的主要内容,如果未能解决你的问题,请参考以下文章

python怎么将列表中元素分配成三份(无需连续),列举出所有的方案?

如何将 xml 字符串分成元组?

如何将apache tomcat的server.xml拆分成几个单独的文件?

将168枚月饼平均分成若干盒,每盒可放3到8枚有多少种分法?

HZNU 2019 Summer training 4

将 data.table 链分成两行代码以提高可读性