将大型 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.这并不难改变,但我直接使用文档中的食谱,所以我不必解释如何改变它。
我有一个很大的 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.这并不难改变,但我直接使用文档中的食谱,所以我不必解释如何改变它。