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 データのストリームを処理できるので、XML::Twig を使用することを強くお勧めします。次のように使用できます。

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..

1 つのトリックは、次のようなことです。

my $structure = $link->simplify;

今では、構造に応じて、ハッシュ参照と配列参照が混在しています! 属性も含めてすべてあり、

print Dumper $structure; exit;

また、Data::Dumper を使用して検査し、必要なものを取得できます。

完了したら、フラッシュしてメモリを解放することを忘れないでください。

    $link->flush;
}
于 2009-11-23T10:42:59.543 に答える
5

XML パーサーを使用します。この問題は、イベントベースのパーサーでの解析に非常に適しているため、組み込みのXML::ParserまたはXML::SAXモジュールがどのように機能するかを調べることをお勧めします。処理するタグの種類ごとに 2 つのイベント ハンドラーを作成し、一致するコンテンツを 2 つの個別のファイルに送信できる必要があります。

于 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> cat s1.txt
ここにたくさんのテキスト
ここにたくさんのテキスト
ここにたくさんのテキスト

C:\ Temp> cat s2.txt
ここにもう少し
ここにもう少し
ここにもう少し
于 2009-11-23T17:09:36.583 に答える
4

はい、Perl は (またはおそらく "a") 方法です。

XML パーサーが必要です。CPANにはいくつかの選択肢がありますので、見てください。

XML::LibXML::Parser には、ファイルの一部を解析するための何かがあるように見えます。これは、必要なもののように聞こえます。

于 2009-11-23T10:17:56.817 に答える
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 に答える
1

これらの方法の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の1つがあります

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 パーサーは、ファイルに対して操作を実行する前にメモリ内のファイル全体を必要とするため、大幅な速度低下やアプリケーションのクラッシュを引き起こす可能性があります (高レベルのふわふわしたクラウド開発者は、再帰構造について忘れがちです)。 )。

代わりに、実用的になることができます。データはかなり一貫したパターンに従っているようです。そして、これは 1 回限りの変換です。

次のようなものを試してください


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 : fromとして一致するセクションへの参照を修正しました$2

于 2009-11-23T10:23:17.563 に答える