如何在第n次出现某个标签后将巨大的xml文件拆分为较小的文件?

时间:2017-03-23 19:56:17

标签: xml macos awk

我有一个30 GB的xml文件,我想把它拆分成较小的文件。

文件中的数据如下:

<film>.....</film>
.
.
.
.
.
.
<film>.....</film>

我可以使用“split -l”,但问题是某些电影元素包含带换行符的文本数据。因此,一个电影元素可能需要不止一行。

我想要做的是拆分它,以便每个新的较小文件包含例如3000个电影元素。所以它应该在每3000个电影标签之后拆分......

我正在使用Mac OS X,我想要一个awk解决方案。

我尝试使用此split file on Nth occurrence of delimiter但未成功...在结束电影标签后没有拆分文件......

3 个答案:

答案 0 :(得分:3)

流式传输XSLT 3.0的工作:

<xsl:transform version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:mode streamable="true" on-no-match="shallow-copy"/>    
  <xsl:template match="/*">
    <xsl:for-each-group select="*" group-adjacent="(position()-1) idiv 3000">
      <xsl:result-document href="chunk{position()}.xml">
        <xsl:copy>
          <xsl:copy-of select="."/>
        </xsl:copy>
      </xsl:result-document>
    </xsl:for-each-group>
  </xsl:template>
</xsl:transform>

这比awk解决方案更强大,因为它实际上解析了XML,因此它保证了良好的输入和良好的输出。当您处理30Gb时,您无法手动检查输出,因此如果您未能预测输入中可能出现的所有内容(例如标题中带有“电影”的电影),则存在未检测到的垃圾的严重危险。因此,正确处理标记的结构会更加安全。

另一件事是,如果你的输入是格式良好的XML,它在<film>元素周围有一个包装元素,如果要将输出作为XML处理,它将需要一个类似的包装元素。 XSLT解决方案免费处理。

您可能已经注意到,此样式表可以将任何xml文件拆分为块,当然块大小可以很容易地作为参数提供。

答案 1 :(得分:2)

这可能是你正在寻找的东西:

awk '{ gsub(/@/,"@A"); gsub(/}/,"@B"); gsub(/<\/film>\n?/,"}") } 1' file |
awk -v RS='}' -v ORS='</film>' '
    (NR%3000)==1 { close(out); out="out"++cnt }
    { gsub(/@B/,"}"); gsub(/@A/,"@"); print > out }
'

但没有样本输入/输出,这是一个猜测,当然,未经测试。

答案 2 :(得分:0)

当Ed Morton发布awk解决方案时,它通常是像我这样的低级用户的小教程......

但无论如何,因为我在过去的一个半小时里一直在做这个练习,所以我想冒风险发布这个解决方案,这是一个转变by the link you already found

$ awk '$0 ~/<film.*>/{++delim} {file = sprintf("chunk%s", int(delim/7)); print >file; }' file4 

<强>测试
我使用一个小的bash循环来创建一个包含50条记录的小电影文件,然后将这些电影拆分为7进行测试:

$ for ((i=1;i<50;i++));do echo -e "<film$i>..............</film$i>" >>file4;done
$ head file4
<film1>..............</film1>
<film2>..............</film2>
<film3>..............</film3>
<film4>..............</film4>
<film5>..............</film5>
<film6>..............</film6>
<film7>..............</film7>
<film8>..............</film8>
<film9>..............</film9>
<film10>..............</film10>

$ awk '$0 ~/<film.*>/{++delim} {file = sprintf("chunk%s", int(delim/7)); print >file; }' file4 
$ cat chunk0
<film1>..............</film1>
<film2>..............</film2>
<film3>..............</film3>
<film4>..............</film4>
<film5>..............</film5>
<film6>..............</film6>

另一个测试,每部电影都有一些换行符:

$ for ((i=1;i<50;i++));do echo -e "<film$i>...\n...\n...\n.....</film$i>" >>file4;done
$ head -n20 file4
<film1>...
...
...
.....</film1>
<film2>...
...
...
.....</film2>
<film3>...
...
...
.....</film3>
<film4>...
...
...
.....</film4>
<film5>...
...
...
.....</film5>


$ awk '$0 ~/<film.*>/{++delim} {file = sprintf("chunk%s", int(delim/7)); print >file; }' file4 

$ ls chunk*
chunk0  chunk1  chunk2  chunk3  chunk4  chunk5  chunk6  chunk7

$ cat chunk1
<film7>...
...
...
.....</film7>
<film8>...
...
...
.....</film8>
<film9>...
...
...
.....</film9>
<film10>...
...
...
.....</film10>
<film11>...
...
...
.....</film11>
<film12>...
...
...
.....</film12>
<film13>...
...
...
.....</film13>

嗯,在这两种情况下似乎都能正常工作。请注意,在此配置中,输入文件每7部分分割一次 - 而不是每7行。您可以将此数字更改为任何内容。