我试图将一个大的XML文件分割成多个文件,并在AWK脚本中使用了下面的代码。
/<fileItem>/ { rfile="fileItem" count ".xml" print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > rfile print $0 > rfile getline while ($0 !~ "<\/fileItem>" ) { print > rfile getline } print $0 > rfile close(rfile) count++ }
上面的代码生成一个名为“fileItem_1”,“fileItem_2”,“fileItem3”等的XML文件列表
不过,我希望文件名是类似于“item_XXXXX”,其中XXXXX是XML内的节点 – 如下所示
<fileItem> <id>12345</id> <name>XXXXX</name> </fileItem>
所以,基本上我想“id”节点是文件名。 任何人都可以帮我这个吗?
我不会使用getline
。 (我甚至在一本AWK书中读过,不推荐使用它)。我认为,使用全局变量的状态更简单。 (具有全局变量的表达式也可以在模式中使用。)
脚本可能是这样的:
test-split-xml.awk
:
/<fileItem>/ { collect = 1 ; buffer = "" ; file = "fileItem_"count".xml" ++count } collect > 0 { if (buffer != "") buffer = buffer"\n" buffer = buffer $0 } collect > 0 && /<name>.+<\/name>/ { # cut "...<name>" i = index($0, "<name>") ; file = substr($0, i + 6) # cut "</name>..." i = index(file, "</name>") ; file = substr(file, 1, i - 1) file = file".xml" } /<\/fileItem>/ { collect = 0; print file print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" >file print buffer >file }
我准备了一些小样本的样本数据:
test-split-xml.xml
:
<?xml version=\"1.0\" encoding=\"UTF-8\"?> <top> <some> <fileItem> <id>1</id> <name>X1</name> </fileItem> </some> <fileItem> <id>2</id> <name>X2</name> </fileItem> <fileItem> <id>2</id> <!--name>X2</name--> </fileItem> <any> other input </any> </top>
…并得到以下输出:
$ awk -f test-split-xml.awk test-split-xml.xml X1.xml X2.xml fileItem_2.xml $ more X1.xml <?xml version="1.0" encoding="UTF-8"?> <fileItem> <id>1</id> <name>X1</name> </fileItem> $ more X2.xml <?xml version="1.0" encoding="UTF-8"?> <fileItem> <id>2</id> <name>X2</name> </fileItem> $ more fileItem_2.xml <?xml version="1.0" encoding="UTF-8"?> <fileItem> <id>2</id> <!--name>X2</name--> </fileItem> $
tripleee的评论是合理的。 因此,这种处理应该限于个人使用,因为不同的(和合法的)XML文件的格式化可能会在这个脚本处理中造成错误。
你会注意到,整个脚本中没有next
。 这是故意的。
首先 – 你需要一个解析器。
XML是一种上下文数据格式。 正则表达式不是。 所以你永远不能使一个正则表达式的基础处理系统实际上正常工作。
这只是坏消息
但解析器确实存在,而且它们很容易处理。 我可以给你一个更好的数据输入例子。 但我会使用XML::Twig
和perl
来做到这一点:
#!/usr/bin/env perl use strict; use warnings; use XML::Twig; #subroutine to extract and process the item sub save_item { my ( $twig, $item ) = @_; #retrieve the id my $id = $item -> first_child_text('id'); print "Got ID of $id\n"; #create a new XML document for output. my $new_xml = XML::Twig -> new; $new_xml -> set_root (XML::Twig::Elt -> new ( 'root' )); #cut and paste the item from the 'old' doc into the 'new' #note - "cut" applies to in memory, #not the 'on disk' copy. $item -> cut; $item -> paste ( $new_xml -> root ); #set XML params (not strictly needed but good style) $new_xml -> set_encoding ('utf-8'); $new_xml -> set_xml_version ('1.0'); #set output formatting $new_xml -> set_pretty_print('indented_a'); print "Generated new XML:\n"; $new_xml -> print; #open a file for output open ( my $output, '>', "item_$id.xml" ) or warn $!; print {$output} $new_xml->sprint; close ( $output ); } #create a parser. my $twig = XML::Twig -> new ( twig_handlers => { 'fileItem' => \&save_item } ); #run this parser on the __DATA__ filehandle below. #you probably want parsefile('some_file.xml') instead. $twig -> parse ( \*DATA ); __DATA__ <xml> <fileItem> <id>12345</id> <name>XXXXX</name> </fileItem> </xml>
随着XML::Twig
来xml_split
这可能适合您的需求
如果你的XML真的很好的形成和一致的,那么你所需要的就是:
awk -F'[<>]' ' /<fileItem>/ { header="<?xml version=\"1.0\" encoding=\"UTF-8\"?>" ORS $0; next } /<id> { close(out); out="item_" $3; $0=header ORS $0 } { print > out } ' file
以上是没有经过测试的,因为您没有提供样本输入/输出以供我们测试可能的解决方案。