我正在尝试使用Perl编辑文本。我需要进行替换,但是一旦在文本中找到特定的单词,就无法应用替换。所以,想象一下我想用“再见”替换所有“你好”的形式,但是一旦找到“foo”这个词就不能应用替换。
我试着这样做:
use warnings;
use strict;
$/ = undef;
my $filename = shift;
open F, $filename or die "Usa: $0 FILENAME\n";
while(<F>) {
do {s/hello/goodbay/} until (m{foo});
print;
}
close F;
但是,结果只改变了我文本的第一个“你好”。
有什么建议吗?
答案 0 :(得分:4)
试着想一想什么是最有效率的。它应该是以下之一:
s{^(.*?)(foo|\z)}{
my $s = $1;
$s =~ s{hello}{goodbay}g;
$s.$2
}se;
print;
或(与上述相同,但需要5.14 +)
s{^(.*?)(foo|\z)}{ s{hello}{goodbay}gr . $2 }se;
print;
或
my $pos = /foo/ ? $-[0] : length;
my $s = substr($_, 0, $pos, '');
$s =~ s{hello}{goodbay}g;
print($s);
print;
即使foo
不存在,两者都有效。
此解决方案使用更少的内存:
# Assumes foo will always be present
# (though it could be expanded to handle that
# Assumes foo isn't a regex pattern.
local $/ = "foo";
$_ = <$fh>;
chomp;
s{hello}{goodbay}g;
print;
print $/;
local $/;
print <$fh>;
答案 1 :(得分:0)
如果您处理的子字符串(示例的hello和foo)是单个单词,则可以轻松地将$/ = undef;
替换为$/ = " ";
。目前,您一次在整个文件中啜饮,这意味着while循环最多只执行一次。
这是因为在告诉perl没有行分隔符之后,整个输入中只有一个“行”。
如果您使用空格作为输入分隔符,它将逐字循环输入,并希望按预期工作。
答案 2 :(得分:0)
使用标志变量:
use warnings;
use strict;
my $filename = shift;
open F, $filename or die "Usa: $0 FILENAME\n";
my $replace=1;
while(<F>) {
$replace = 0 if m{foo};
s/hello/goodbye/g if $replace;
print;
}
close F;
在包含结束模式的行停止。如果你想在比赛前替换,那将会稍微复杂一些。
答案 3 :(得分:0)
此答案使用Perl 5.10中引入的${^PREMATCH]
和相关变量。
#!/usr/bin/env perl
use v5.10.0;
use strict;
use warnings;
my $foo_found;
while (my $line = <>) {
if (!$foo_found) {
if ($line =~ m/foo/ip) {
# only replace hellos in the part before foo
${^PREMATCH} =~ s/hello/goodbye/g;
$line = "${^PREMATCH}${^MATCH}${^POSTMATCH}";
$foo_found ++;
} else {
$line =~ s/hello/goodbye/ig;
}
}
print $line;
}
给出以下输入:
hello cruel world
hello baseball
hello mudda, hello fadda
foo
The rest of the hellos should stay
Last hello
我得到以下输出
goodbye cruel world
goodbye baseball
goodbye mudda, goodbye fadda
foo
The rest of the hellos should stay
Last hello
如果你没有5.10,你可以使用$`
及相关变量,但它们会带来性能损失。有关详细信息,请参阅perldoc perlvar
。