根据标签数将单个 XML 文件拆分为 2 个文件

split a single XML file into 2 files based on tag count

我有一个 XML 文件,它由许多 XML 条记录组成,这些记录由如下标记限定:

<PubmedArticle>  
 <MedlineCitation Status="MEDLINE" Owner="NLM">
      <PMID Version="1">24273127</PMID>
...
...
...

</PubmedArticle>
<PubmedArticle>  
 <MedlineCitation Status="MEDLINE" Owner="NLM">
      <PMID Version="1">24273128</PMID>
...
...
...

</PubmedArticle>
<PubmedArticle>  
 <MedlineCitation Status="MEDLINE" Owner="NLM">
      <PMID Version="1">24273129</PMID>
...
...
...

</PubmedArticle>

我可以使用

生成单个文件
awk '/<PubmedArticle/{f=1; out="mysmallfile_"(++c)".xml"} f{print > out} /<\/PubmedArticle>/{close(out); f=0}' mylargefile

如何生成由特定数量的记录组成的文件?例如,假设我有一个包含 1000 XML 条记录的大型 XML 文件,我想创建 2 x 500 XML 条记录文件?

我认为 awk 应该保存到一个文件,直到它满足定义的标签匹配数,然后再保存到另一个文件。

这部分

/<PubmedArticle/{f=1; out="mysmallfile_"(++c)".xml"}

可以修改为通过利用整数除法给出相同的 out n 次,例如对于 n=3 它将是

/<PubmedArticle/{f=1; out="mysmallfile_"int(c++/3)".xml"}

这将为匹配 <PubmedArticle

的后续行提供
mysmallfile_0.xml
mysmallfile_0.xml
mysmallfile_0.xml
mysmallfile_1.xml
mysmallfile_1.xml
mysmallfile_1.xml
mysmallfile_2.xml
mysmallfile_2.xml
mysmallfile_2.xml

等等。请注意,我使用 c++ 而不是 ++c,因为稍后使用会首先重复 n-1 次,然后 n 次。

这部分

/<\/PubmedArticle>/{close(out); f=0}

可以使用除法余数进行如下调整

/<\/PubmedArticle>/&&c%3==0{close(out); f=0}

c3 整除时,附加条件成立,这仅适用于给定 out.

的最后一次使用

(在 GNU Awk 5.0.1 中测试)

你的问题和你引用的问题之间的区别在于,他们的输入中有块,他们不想出现在输出中,而你希望输入的每一行都出现在输出文件中,因此,虽然他们只需要打印开始和结束标记之间的内容,因此必须对两者进行测试,但您没有这个问题,只需要测试开始或结束标记以确定何时更改输出文件。

使用任何 awk:

$ awk -v maxRecs=2 '
    /<PubmedArticle>/ && ((++recNr % maxRecs) == 1) {
        close(out); out="mysmallfile_" (++fileNr) ".xml"
    }
    { print > out }
' file

$ head mysmallfile*
==> mysmallfile_1.xml <==
<PubmedArticle>
 <MedlineCitation Status="MEDLINE" Owner="NLM">
      <PMID Version="1">24273127</PMID>
...
...
...

</PubmedArticle>
<PubmedArticle>
 <MedlineCitation Status="MEDLINE" Owner="NLM">

==> mysmallfile_2.xml <==
<PubmedArticle>
 <MedlineCitation Status="MEDLINE" Owner="NLM">
      <PMID Version="1">24273129</PMID>
...
...
...

</PubmedArticle>

或使用 GNU awk 用于 multi-char RS 和 RT:

$ awk -v maxRecs=2 -v RS='</PubmedArticle>\n' -v ORS= '
    (NR % maxRecs) == 1 {
        close(out); out="mysmallfile_" (++fileNr) ".xml"
    }
    RT { print [=12=] RT > out }
' file

$ head mysmallfile*
==> mysmallfile_1.xml <==
<PubmedArticle>
 <MedlineCitation Status="MEDLINE" Owner="NLM">
      <PMID Version="1">24273127</PMID>
...
...
...

</PubmedArticle>
<PubmedArticle>
 <MedlineCitation Status="MEDLINE" Owner="NLM">

==> mysmallfile_2.xml <==
<PubmedArticle>
 <MedlineCitation Status="MEDLINE" Owner="NLM">
      <PMID Version="1">24273129</PMID>
...
...
...

</PubmedArticle>

我正在调用 close() 关闭每个输出文件,因为我们要避免大多数 awk 可能出现的“打开文件过多”错误或 gawk 变慢。

以上假定您的目标是创建尽可能多的“maxRecs”长度的文件,然后将输入的剩余部分放入最后一个文件中,因此如果您有一个包含 800 条记录的输入文件,您我将得到一个包含 500 条记录的输出文件和另一个 300 条记录的输出文件,而不是两个 400 条记录的输出文件。