如何将这个perl one-liner转换成脚本(特别是多行,全局正则表达式替换)

Evi*_*ady 2 regex perl replace

我有一个包含多个XML标签的文件,如下所示:

<Good>Yay!</Good>
<Great>Yup!</Great>
<Bad>booo</Bad>
<Bad>
<Ok>not that great</ok>
</Bad>
<Good>Wheee!</Good>
Run Code Online (Sandbox Code Playgroud)

我想摆脱"坏"标签和其他任何东西.所以它会变成:

<Good>Yay!</Good>
<Great>Yup!</Great>
<Good>Wheee!</Good>
Run Code Online (Sandbox Code Playgroud)

我知道这个单行:

perl -pe "undef $/;s/<Bad>.*?<\/Bad>//msg" < originalFile > newlyStrippedFile
Run Code Online (Sandbox Code Playgroud)

似乎做我想做的一切(除了添加额外的换行符,但希望我能够轻松地处理它)

但是我需要把它放在一个脚本中(两个文件被读入命令行,一个带有所有标签,另一个带有要拔出的标签列表),所以同样的事情会被多次调用.

而我只是遇到了麻烦.要么它只读一行,要么我得错误或两者兼而有之.

以下是我最近尝试的相关部分:

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);  

}
Run Code Online (Sandbox Code Playgroud)

这让我"在我的$ sdb_input_scalar =〜行中使用未初始化的值$ sdb_input_scalar替换(s ///).并且仅为输入打开Filehandle NEWLY_STRIPPED_FILE

当然,我的两个文件看起来仍然相同,好像我没有对他们做任何事情.

对不起,如果我遗漏了一些明显的东西,但我对perl来说真的很陌生.有人在工作时给出了8小时的估计来完成这个脚本,我已经用了超过5个小时来安装perl,学习语法并让其他方面正确.我知道有一个XML :: Parser模块,但我发现这些例子在我完成的短时间内非常压倒性.

我必须假设我的正则表达式是正确的,因为单线程工作得非常好.谁能帮助我适应我需要的东西?

Ili*_*ion 6

你真的应该使用XML解析器.这几乎可以保证XML文件不会像你期望的那样解析正则表达式.但是,让我们先开始吧.

你在哪里:

@sdb_input_scalar=join("",@sdb_input_array);
Run Code Online (Sandbox Code Playgroud)

你真的想要:

$sdb_input_scalar=join("",@sdb_input_array);
Run Code Online (Sandbox Code Playgroud)

现在一些其他提示.

在脚本的顶部,确保使用-w标志启用警告,如下所示:

#!/path/to/perl -w

use strict;
Run Code Online (Sandbox Code Playgroud)

一旦你加入use strict它会导致你的几个错误,但这是一件好事.我们将强制执行一些范围和其他良好实践.您现在需要使用my初始化变量(以$,@或%开头).例如:

my @sdb_input_array = <ORIGINAL_FILE>;
Run Code Online (Sandbox Code Playgroud)

要么:

foreach my $tag (@tags) { ... }
Run Code Online (Sandbox Code Playgroud)

不要像你一样打开电话,而是使用三个争论版本:

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

my @sdb_input_array = <$originalFile>;
Run Code Online (Sandbox Code Playgroud)

这将把它设置为只读.见http://perldoc.perl.org/functions/open.html

通常你应该避免依赖于全局变量.更改您调用remove_tag()的方式:

foreach $tag (@tags) {
  $sdb_input_scalar = remove_tag($sdb_input_scalar, $tag);
}
Run Code Online (Sandbox Code Playgroud)

为了支持这一点,您还需要更改功能:

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

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

   return $input;    
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过在remove_tag函数之外移动它来迭代所有标记后写出一次:

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

   print $strippedFile $sdb_input_scalar;
   close($strippedFile);
Run Code Online (Sandbox Code Playgroud)