将大型 xml 文件分成 n 组

Splitting large xml files in n groups

我有一个很大的 xml 文件,其父标签有 97k 个子标签。我想分成 10 个文件,每个文件有 10k 个标签,最后一个文件有剩余的标签。

我有这段代码可以将一个子标签写入每个文件,但无法提出组。

假设我的样本 xml 有 10 个子标签,我想创建 5 个文件,每个文件有 2 个子标签。

我的样本xml:

<root>
    <row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
</root>

我的结果应该是 5 个文件,每个文件有 2 个条目,如下所示:

<root>
        <row>
            <NAME>A</NAME>
            <FIRSTNAME>A</FIRSTNAME>
            <GENDER>M</GENDER>
        </row>
        <row>
            <NAME>B</NAME>
            <FIRSTNAME>B</FIRSTNAME>
            <GENDER>M</GENDER>
        </row>
</root>

下面的代码将每个子标签放在每个文件中,但我想在这里举个例子,每个文件有 2 个标签。

import xml.etree.ElementTree as ET
context = ET.iterparse('file.xml', events=('end', ))
index = 0
for event, elem in context:
    if elem.tag == 'row':
        index += 1
        filename = format(str(index) + ".xml")
        with open(filename, 'wb') as f:
            f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
            f.write(ET.tostring(elem))

提前致谢!

编辑以添加食谱:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

你有一个可迭代的(事件,元素)对:

context = ET.iterparse('file.xml', events=('end', ))

现在,您想将其过滤为 row 个元素:

rows = (elem for event, elem in context if elem.tag == 'row')

现在您想将它们分组。使用 the grouper recipe from the itertools docs:

groups = grouper(rows, 2)

您显然可以将 2 更改为 1000 或其他任何东西,一旦您开始工作并想要 运行 它是真实的。

现在,您可以迭代这些组。当我们这样做的时候,让我们使用 enumerate so you don't need that manual index += 1 stuff. Also, instead of building a string manually and then pointlessly calling format on it, let's just use an f-string.

for index, group in enumerate(groups):
    # If you need to run on 3.5 or 2.7, use "{}.xml".format(index)
    filename = f"{index}.xml"
    with open(filename, 'wb') as f:
        f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")

…然后迭代组内的元素——但要小心;如果您有奇数个元素,grouper 将用 None 个值填充不完整的最后一组。1

        for elem in group:
            if elem:
                f.write(ET.tostring(elem))

1.这并不难改变,但我直接使用文档中的食谱,所以我不必解释如何改变它。