2

次のようないくつかの XML タグを含むファイルがあります。

<Good>Yay!</Good>
<Great>Yup!</Great>
<Bad>booo</Bad>
<Bad>
<Ok>not that great</ok>
</Bad>
<Good>Wheee!</Good>

「悪い」タグとその間のものを取り除きたい場所。したがって、次のようになります。

<Good>Yay!</Good>
<Great>Yup!</Great>
<Good>Wheee!</Good>

私はこのワンライナーを知っています:

perl -pe "undef $/;s/<Bad>.*?<\/Bad>//msg" < originalFile > newlyStrippedFile

がやりたいことはすべてやっているようです(余分な改行を入れることを除いて、うまくいけば、それを十分に簡単に処理できます)

しかし、それをスクリプトに入れる必要があります (2 つのファイルがコマンド ラインに読み込まれ、1 つはすべてのタグを含み、もう 1 つは抽出するタグのリストを含みます)、同じことが何度か呼び出されることになります。

そして、私はちょうど問題を抱えています。1行しか読み取っていないか、エラーが発生するか、またはその両方です。

私の最近の試みの関連部分は次のとおりです。

open ORIGINAL_FILE, $sdb_pathname
  or die "Can't open '$sdb_pathname' : $!";

@sdb_input_array = <ORIGINAL_FILE>;  

close ORIGINAL_FILE;
@sdb_input_scalar=join("",@sdb_input_array);

foreach $tag (@tags) {
  &remove_tag($tag);
}

sub remove_tag 
{
   my($current_tag) = @_;

   $sdb_input_scalar  =~ s/<$current_tag>.*?<\/$current_tag>//msg; 

   open NEWLY_STRIPPED_FILE, $clean_sdb_pathname
     or die "Can't open '$clean_sdb_pathname' : $!";

   print(NEWLY_STRIPPED_FILE $sdb_input_scalar);
   close(NEWLY_STRIPPED_FILE);  

}

これにより、「私の $sdb_input_scalar =~ 行で、初期化されていない値 $sdb_input_scalar を置換 (s///) で使用することができます。また、ファイルハンドル NEWLY_STRIPPED_FILE が入力用にのみ開かれます」

もちろん、私の 2 つのファイルは、何もしていないかのように、同じように見えます。

明らかな何かが欠けている場合は申し訳ありませんが、私は文字通り perl の初心者です。職場の誰かがこのスクリプトを実行するのに 8 時間かかると見積もっていましたが、私は perl のインストール、構文の学習、およびその他の側面を正しく行うためだけに、すでに 5 時間以上を費やしました。XML::Parser モジュールがあることは知っていますが、完了するまでに残された短い時間のために、例が非常に圧倒されていることがわかりました。

ワンライナーがうまく機能するため、正規表現が正しいと想定する必要があります。誰かが私が必要とするものに適応させるのを手伝ってくれませんか?

4

5 に答える 5

6

本当にXMLパーサーを使用する必要があります。XMLファイルが正規表現で期待したとおりに解析されないことはほぼ保証されています。ただし、最初に始めましょう。

あなたが持っている場所:

@sdb_input_scalar=join("",@sdb_input_array);

あなたが実際に欲しい:

$sdb_input_scalar=join("",@sdb_input_array);

今、いくつかの他のヒント。

スクリプトの上部で、次のように-wフラグを使用して警告を有効にしていることを確認してください。

#!/path/to/perl -w

use strict;

追加すると、use strictいくつかのエラーが発生しますが、それは良いことです。いくつかの範囲と他のグッドプラクティスを実施します。ここで、変数($、@、または%で始まる)をmyで初期化する必要があります。例えば:

my @sdb_input_array = <ORIGINAL_FILE>;

また:

foreach my $tag (@tags) { ... }

あなたのようにオープンと呼ぶ代わりに、3つの引数バージョンを使用してください:

open ($originalFile, "<", $sdb_pathname)
  or die "Can't open '$sdb_pathname' : $!";

my @sdb_input_array = <$originalFile>;

これにより、読み取り専用に設定されます。http://perldoc.perl.org/functions/open.htmlを参照してください

通常、グローバルへの依存は避けてください。remove_tag()の呼び出し方法を変更します。

foreach $tag (@tags) {
  $sdb_input_scalar = remove_tag($sdb_input_scalar, $tag);
}

これをサポートするには、関数も変更する必要があります。

sub remove_tag 
{
   my($input, $current_tag) = @_;

   $input  =~ s/<$current_tag>.*?<\/$current_tag>//msg; 

   return $input;    
}

これをremove_tag関数の外に移動することにより、すべてのタグを繰り返し処理した後、一度書き出すことができます。

   open ($strippedFile, ">", $clean_sdb_pathname)
     or die "Can't open '$clean_sdb_pathname' : $!";

   print $strippedFile $sdb_input_scalar;
   close($strippedFile);
于 2012-12-18T09:56:37.657 に答える
2

まず、XML を処理するために正規表現を使用しないでください。次に、特定の使用例ではなく、質問のタイトルから疑問を推測します。あなたのワンライナーは次のように書く方が良いです:

perl -0777 -pe "s/<(Bad)>.*?<\/\1>//msg" < originalFile > newlyStrippedFile

ここで、Perl 自体を使用してワンライナーを「膨張」させます。

perl -MO=Deparse -0777 -pe "s/<(Bad)>.*?<\/\1>//msg" > oneliner.pl

そして、これはあなたが得るものです:

BEGIN { $/ = undef; $\ = undef; }
LINE: while (defined($_ = <ARGV>)) {
    s[<(Bad)>.*?</\1>][]gms;
}
continue {
    die "-p destination: $!\n" unless print $_;
}

追加するだけuse strict; use warnings;です。

于 2012-12-18T11:16:46.020 に答える
2

を使用したソリューションは次のXML::Twigとおりです。

use warnings;
use strict;

use XML::Twig;

my $xml = XML::Twig->new(
    pretty_print  => 'indented',
    twig_handlers => {
            #Define a sub that will be called for all 'Bad' tags
            Bad => sub {
                $_->set_tag('Good'); 
        }
    }
);

$xml->parse(\*DATA);
$xml->print;

__DATA__
<xml><Good>Yay!</Good><Great>Yup!</Great><Bad>booo</Bad><Bad>
<Ok>not that great</Ok></Bad><Good>Wheee!</Good></xml>

XML::Twigファイル名を直接取得して処理するメソッドもありparsefile()parsefile_inplace()必要なものだけです。

この方法には多少の学習曲線がありますが、メリットは大きいです。

于 2012-12-18T10:30:49.587 に答える
0

を使った解決法XML::Twigです。あなたの XML ドキュメントは整形式であり、その中に表示されたデータを<root>要素でラップしていると想定しています。

この$twigオブジェクトは、要素に対して 1 つのtwig ハンドラーを定義し<Bad>ます。これは、解析中に要素が表示された場合に要素を削除するだけです。

入力が解析されると$twig-print、残りの XML が表示されます。

use strict;
use warnings;

use XML::Twig;

my $twig = XML::Twig->new(
  twig_handlers => { Bad => sub { $_->delete } },
  pretty_print => 'record',
);

$twig->parse(<<'END_XML');

<root>
  <Good>Yay!</Good>
  <Great>Yup!</Great>
  <Bad>booo</Bad>
  <Bad>
    <Ok>not that great</Ok>
  </Bad>
  <Good>Wheee!</Good>
</root>

END_XML

$twig->print;

出力

<root>
  <Good>Yay!</Good>
  <Great>Yup!</Great>
  <Good>Wheee!</Good>
</root>
于 2012-12-18T19:20:04.970 に答える
-1

これでうまくいくはずです:

    $tags=join("",@sdb_input_array);
    print "contents before : $tags \n";
    $tags =~ s/<Bad>.*?<\/Bad>//msg;
    print "content cleaned : $tags \n";

タグ変数に「BAD」タグを付けないようにする必要があります。唯一の問題は、タグ行に空白の未入力行が残り、GOODタグ行の間に空白行があることです。ただし、空白行は次のように削除できます。あなたの最後のステップ

于 2012-12-18T11:28:42.733 に答える