1

以下を使用して、XML タグ間のコンテンツを抽出しています。

perl -lne 'BEGIN{undef $/} while (/<tagname>(.*?)<\/tagname>/sg){print $1}' input.txt > output.txt

残念ながらout of memory問題が発生しています。ファイルを分割してそれぞれを処理してから連結できることはわかっていますが、上記を変更するか、awk や sed などを使用するか、別の方法があるかどうか疑問に思いました。

input.txtファイルサイズは 17GB から 70GB の間で異なります。

編集:

入力ファイルは任意の XML ファイルにすることができます。注意すべき点は、改行が含まれていないことです。例: -

<body><a></a><b></b><c></c></body><foo></foo><bar><z></z></bar>

4

6 に答える 6

3

ファイルから小さいサイズのチャンクを読み取るために、入力レコード区切り文字を終了タグに設定できます。

BEGIN { $/ = "</tagname>"; }

次に例を示します。

コード:

perl -lnwe 'BEGIN { $/ = "</tagname>"; } print;'

入力:

<tagname>foo</tagname><tagname>bar</tagname><tagname>baz</tagname><tagname>baf</tagname>

出力:

<tagname>foo
<tagname>bar
<tagname>baz
<tagname>baf

終了タグが欠落していることに注意してください。これは-l、使用するオプションにchomp、入力レコード区切り文字を削除するが含まれているためです。この動作を望まない場合は、-lオプションを削除して、printステートメントに改行を挿入してください。

ノート:

これは多少のハックだと思いますが、すでに使用しているものと一致します。つまり、大文字と小文字を区別して正確なタグを一致させます。

補償するためにできることは、この中で正規表現を使用することです。

perl -lnwe 'BEGIN { $/ = "</tagname>"; } 
    while (/<tagname>(.*?)<\/tagname>/sg) { print $1 }' input.txt > output.txt

または、XMLパーサーを使用してチャンクを解析することもできます。

他の人が提案したXMLパーサーがそのような巨大なファイルに対して機能しない場合、これはタグを半分にカットするリスクを冒さずにデータの小さなチャンクを読み取る方法になります。

于 2012-08-22T12:09:44.370 に答える
3

巨大なファイルの解析は、 XML :: LibXML::Readerのようなプルパーサーで可能になるはずです。次に例を示します。

#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML::Reader;

my $reader = XML::LibXML::Reader->new(location => 'input.txt') or die;

while ($reader->read) {
    if ($reader->nodePath =~ m{/tagname$}                    # We are at <tagname> or </tagname>.
        and $reader->nodeType == XML_READER_TYPE_ELEMENT) {  # Only the start tag is interesting.
        print $reader->readInnerXml;
    }
}
于 2012-08-22T11:08:15.090 に答える
3

このワンライナーは、ファイル全体を 1 つの巨大な「行」としてメモリに読み込みます。もちろん、17GB以上を詰め込むとメモリに問題が発生します! ファイルを 1 行ずつ読み取って処理するか、read代わりに適切なサイズのチャンクを取得するために使用します。

この場合、 を検索し<tagname>、行内のその位置に注意して、そこから開始する終了タグを検索します。見つからなかった場合は、現在の行/チャンクをバッファに詰め込み、ファイル内のさらに別の行で見つかるまで繰り返します。見つかったら、このバッファを出力して空にします。ファイルの終わりまで繰り返します。

任意のサイズのチャンクを使用する場合は、チャンクの末尾から不完全なタグを切り取り、「処理する」バッファに詰め込むことで、タグが境界で分割される可能性を考慮しなければならないことに注意してください。

于 2012-08-22T10:19:00.890 に答える
1

awkを使用して、大きな1行のファイルを分割することもできます。Sedは、フルラインをロードしようとするとメモリ不足でバストしますが、awk(perlの場合のように)では、問題を回避して、「改行」として使用するものを定義できます。

perlの場合、上記の例はすでに1つありますが、これがawkの例です。

cat big-one-line-file |  awk 'BEGIN { RS=">" } ; {print $0">"}'

ファイルの終わりに、ファイルが> ""で終わっていない場合、1つの余分な>が表示されることに注意してください。なんらかの方法で削除するか(ポストクリーニングsed:sed '$ s/>$//'など)、スクリプトを調整できます。

私もこの問題を抱えていたので、他の人を助けるために、テストに役立つ例をさらに追加します。

ddを使用してスクリプトをテストし、ファイルの小さな部分を抽出して、作品やタグなどのより大きな「レコード区切り文字」をキャッチできます。例:

dd if=big-one-line-file.xml bs=8192 count=10  | awk ' BEGIN { RS="<tag 123>" } ; NR>1 {print "<tag 123>"$0}  ; NR==1 {print $0}  ' 

big-one-line-file.xmlの最初の80kBを抽出し、ファイルを「」で区切ります。ファイルの先頭にある余分な(そして間違った) ""を避けるために、別の方法で処理します(つまり、触れないでください)。

ddオプションskip={# of blocks to reach near the file size} を使用して、ファイルの先頭ではなく末尾を抽出します(テールは常に1行しかないため、失敗します)。skip = 100000000を使用し、何かが表示されてブロック番号が調整されるまでゼロの削除を開始しました。

于 2012-08-27T14:15:58.623 に答える
0

入力ファイルが整形式の XML であるかどうかは明確ではありません。あなたが与える例はXMLではありません(ルート要素なし)。ただし、データが XML の場合は、 XML::Twigxml_grepに付属のツールを使用できます。これは、一致した各要素がメモリに収まる限り、任意のサイズのファイルで機能します。xml_grep -r tagname --text_only mybig.xml

これが遅すぎる場合は、XML::Parser を直接使用することで速度を上げることができます。コードを書くのはそれほど複雑ではありません。書く必要がない方が簡単ですが;--)

于 2012-08-22T13:52:58.157 に答える
0

入力ファイルにフィルターを適用して、改行を導入します。多分それぞれの後</tagname>?そうすれば、「合理的な」レコードを処理することBEGIN{undef $/}で、コマンドを取り除き、メモリの問題を回避できます。perl

于 2012-08-22T10:31:36.617 に答える