我有一个包含多个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
似乎做我想做的一切(除了添加额外的新行,但希望我能够轻松地处理它)
但是我需要把它放在一个脚本中(两个文件被读入命令行,一个带有所有标签,另一个带有要拔出的标签列表),所以同样的事情将被称为几个次。
我遇到了麻烦。要么它只读一行,要么我得错误,或两者兼而有之。
以下是我最近尝试的相关部分:
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 ///)。 和 Filehandle NEWLY_STRIPPED_FILE仅为输入
打开当然,我的两个文件看起来仍然相同,好像我没有对他们做任何事情。
如果我遗漏了一些明显的东西,我很抱歉,但我对perl来说真的很陌生。有人在工作时给出了8小时的估计来完成这个脚本,我已经用了超过5个小时来安装perl,学习语法并让其他方面正确。我知道有一个XML :: Parser模块,但我发现这些例子在我完成的短时间内非常难以理解。
我必须假设我的正则表达式是正确的,因为单线程工作得非常好。 任何人都可以帮助我根据我的需要进行调整吗?
答案 0 :(得分: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) { ... }
不要像你一样打开电话,而是使用三个争论版本:
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);
答案 1 :(得分: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()
方法,可直接获取文件名并进行处理 - 正是您所需要的。
这种方法有一点学习曲线,但好处很大。
答案 2 :(得分: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;
。
答案 3 :(得分:0)
这是使用XML::Twig
的解决方案。我假设您的XML文档格式正确,并已将您在其中显示的数据包装在<root>
元素中以实现此目的。
$twig
对象为<Bad>
元素定义了一个 twig处理程序,如果元素在解析过程中出现,则只删除该元素。
解析输入后,$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>
答案 4 :(得分:-1)
这应该可以解决问题:
$tags=join("",@sdb_input_array);
print "contents before : $tags \n";
$tags =~ s/<Bad>.*?<\/Bad>//msg;
print "content cleaned : $tags \n";
标签变量现在不应带有“BAD”标签 - 唯一的问题是标签线将留下空白的未填充线,以便在GOOD标记线之间有空行 - 但是你可以删除空行作为你的最后一步