我有一个巨大的文件(500 MB),其组织方式如下:
<link type="1-1" xtargets="1;1">
<s1>bunch of text here</s1>
<s2>some more here</s2>
</link>
<link type="1-1" xtargets="1;1">
<s1>bunch of text here</s1>
<s2>some more here</s2>
</link>
<link type="1-1" xtargets="1;1">
<s1>bunch of text here</s1>
<s2>some more here</s2>
</link>
我想将其转换为一种新格式,其中s1转到一个新文件,每个s1在其自己的行上有一个换行符,而s2转到一个新文件,每个s2在它自己的行上。
Perl是否可以去这里?如果是这样,有人可以告诉我如何实现这一目标吗?
答案 0 :(得分:7)
我热烈建议使用XML :: Twig,因为它能够处理XML数据流。您可以使用以下内容:
use XML::Twig;
my $xml = new XML::Twig( TwigHandlers => { link => \&process_link });
$xml->parsefile('Your file here');
sub process_link
{
my($xml, $link) = @_;
# You can now handle each individual block here..
一个诀窍就是做一些事情:
my $structure = $link->simplify;
现在它是hashrefs和arrayrefs的混合,具体取决于结构!包括属性在内的一切都在那里,
print Dumper $structure; exit;
您可以使用Data :: Dumper检查它以获取您需要的内容。
请记住在完成后将其清除以释放内存。
$link->flush;
}
答案 1 :(得分:5)
使用XML解析器。此问题非常适合使用基于事件的解析器进行解析,因此我建议您查看内置XML::Parser或XML::SAX模块的工作原理。您应该能够为要处理的每种标记创建两个事件处理程序,并将匹配的内容指向两个单独的文件。
答案 2 :(得分:4)
是的,Perl是(也许是“一种”)的方式。
您需要一个XML解析器。有several choices on CPAN所以看看。
XML::LibXML::Parser looks like it has something for parsing parts of files,听起来就像你需要的那样。
答案 3 :(得分:4)
你可以使用Perl,但这不是唯一的方法。这是一个gawk
:
gawk -F">" '/<s[12]>/{o=$0;sub(/.*</,"",$1);print o > "file_"$1 }' file
或者,如果您的任务非常简单,那么:
awk '/<s1>/' file > file_s1
awk '/<s2>/' file > file_s2
或grep
:
grep "<s1>" file > file_s1
grep "<s2>" file > file_s2
答案 4 :(得分:4)
首先,如果您要忽略输入是XML的事实,那么就不需要Perl或Python或gawk或任何其他语言。只需使用
$ grep '<s1>' input_file > s1.txt
$ grep '<s2>' input_file > s2.txt
并完成它。这 似乎 效率低下,但考虑到编写脚本然后调用它所花费的时间,效率低下是无关紧要的。更糟糕的是,如果你不知道如何编写这个特别简单的脚本,你必须在SO上发帖并等待一个超过grep
解决方案效率低下许多数量级的答案。
现在,如果输入是XML的事实是最重要的,那么你应该使用XML解析器。与the incorrect claim made elsethread相反,有大量的XML解析器不必将整个文件加载到内存中。这样的解析器具有可扩展和正确的优点。
我在下面给出的示例旨在复制answer you have already accepted的结构,以表明使用正确的解决方案并不复杂。
为了给予公平警告,下面的脚本可能是 最慢 可能的方式。我写它是为了完全模仿已接受的解决方案。
#!/usr/bin/perl
use strict; use warnings;
use autodie;
my %fh = map { open my $f, '>', $_; $_ => $f } qw{ s1.txt s2.txt };
use HTML::TokeParser::Simple;
my $parser = HTML::TokeParser::Simple->new(\*DATA);
$parser->xml_mode(1);
while ( my $tag = $parser->get_tag('s1', 's2') ) {
my $type = $tag->get_tag;
my $text = $parser->get_text("/$type");
print { $fh{"$type.txt"} } $text, "\n";
}
__DATA__
<link type="1-1" xtargets="1;1">
<s1>bunch of text here</s1>
<s2>some more here</s2>
</link>
<link type="1-1" xtargets="1;1">
<s1>bunch of text here</s1>
<s2>some more here</s2>
</link>
<link type="1-1" xtargets="1;1">
<s1>bunch of text here</s1>
<s2>some more here</s2>
</link>
输出:
C:\Temp> cat s1.txt bunch of text here bunch of text here bunch of text here C:\Temp> cat s2.txt some more here some more here some more here
答案 5 :(得分:1)
您可以使用其中一种方法执行此任务:
答案 6 :(得分:-4)
>> Is perl the way to go here
绝对不是总能走的路。这是Python中的一个
f=open("xmlfile")
out1=open("file_s1","a")
out2=open("file_s2","a")
for line in f:
if "<s1>" in line:
out1.write(line)
elif "<s2>" in line:
out2.write(line)
f.close()
out1.close()
out2.close()
答案 7 :(得分:-5)
如果文件很大,XML解析器可能导致显着减速甚至应用程序崩溃,因为XML解析器在对文件执行任何操作之前需要内存中的整个文件(高级蓬松的云开发人员经常忘记这一点)关于递归结构。)
相反,你可以务实。您的数据似乎遵循相当一致的模式。这是一次性转型。尝试类似
的内容
BEGIN {
open( FOUT1 ">s1.txt" ) or die( "Cannot open s1.txt: $!" );
open( FOUT2 ">s2.txt" ) or die( "Cannot open s2.txt: $!" );
}
while ( defined( my $line = <> ) ) {
if ( $line =~ m{<s1>(.+?)</s1>} ) {
print( FOUT1 "$1\n" );
} elsif ( $line =~ m{<s2>(.+?)</s2>} ) {
print( FOUT2 "$1\n" );
}
}
END {
close( FOUT2 );
close( FOUT1 );
}
然后将此脚本作为perl myscript.pl <bigfile.txt
运行。
更新1 :将来自$1
的匹配部分的引用更正为$2
。