0

我有一个像这样组织的大文件(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 是通往这里的路吗?如果是这样,有人可以告诉我如何做到这一点吗?

4

8 回答 8

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;
}
于 2009-11-23T10:42:59.543 回答
5

使用 XML 解析器。这个问题非常适合使用基于事件的解析器进行解析,因此我建议研究内置XML::ParserXML::SAX模块的工作原理。您应该能够为要处理的每种标签创建两个事件处理程序,并将匹配的内容定向到两个单独的文件。

于 2009-11-23T10:21:32.833 回答
4

首先,如果您要忽略输入是 XML 的事实,那么就不需要 Perl 或 Python 或 gawk 或任何其他语言。只需使用

$ grep '<s1>' input_file > s1.txt
$ grep '<s2>' input_file > s2.txt

并完成它。这似乎效率低下,但考虑到编写脚本然后调用它所花费的时间,效率低下是微不足道的。更糟糕的是,如果您不知道如何编写那个特别简单的脚本,您必须在 SO 上发布并等待一个答案,该答案比grep解决方案的低效率高出许多许多数量级。

现在,如果输入是 XML 的事实一点也不重要,那么您应该使用 XML 解析器。与elsethread 的错误声明相反,有很多 XML 解析器不必将整个文件加载到内存中。这样的解析器将具有可扩展和正确的优点。

我在下面给出的示例旨在复制您已经接受的答案的结构,以向您展示使用正确的解决方案并不复杂。

只是为了给出公平的警告,下面的脚本可能是最慢的方法。我写它是为了完全模仿公认的解决方案。

#!/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> 猫 s1.txt
一堆文字在这里
一堆文字在这里
一堆文字在这里

C:\Temp> 猫 s2.txt
这里还有一些
这里还有一些
这里还有一些
于 2009-11-23T17:09:36.583 回答
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
于 2009-11-23T10:37:22.740 回答
4

是的,Perl 是(或者可能是“一种”)要走的路。

您需要一个 XML 解析器。CPAN 上有多种选择,请看一下。

XML::LibXML::Parser 看起来它有一些用于解析部分文件的东西,这听起来像是你需要的。

于 2009-11-23T10:17:56.817 回答
1

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

  1. 常用表达
  2. HTML::TreeBuilder模块
  3. HTML::TokeParser模块
  4. XML::LibXML模块
于 2009-11-23T21:45:39.007 回答
-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()
于 2009-11-23T12:28:20.767 回答
-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.

于 2009-11-23T10:23:17.563 回答