如何将大文件过滤为两个单独的文件?

时间:2009-11-23 10:15:10

标签: xml perl search text-processing

我有一个巨大的文件(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是否可以去这里?如果是这样,有人可以告诉我如何实现这一目标吗?

8 个答案:

答案 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::ParserXML::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)

您可以使用其中一种方法执行此任务:

  1. Regular expressions
  2. HTML::TreeBuilder模块
  3. HTML::TokeParser模块
  4. XML::LibXML模块

答案 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