我正在通过 Learning Perl ,第9章“使用正则表达式处理文本”。
以下是两章章末练习:
编写一个程序,为目前为止的所有练习答案添加版权行,在“shebang”行后面的文件中放置
## Copyright (c) 20XX by Yours Truly
之类的行。假设将使用文件名调用程序,以便在命令行上进行编辑。- 醇>
修改以前的程序,使其不会编辑已包含版权行的文件。作为提示,您可能需要知道菱形运算符正在读取的文件的名称是$ ARGV。
这是我尝试的解决方案:
#!/usr/bin/env perl
use 5.014;
use warnings;
my $shebang = '(#!/usr/bin/env perl|#!/usr/bin/perl)';
my $copyright = '# Copyright (c) 20XX Yours Truly';
$^I = ".bak";
while (<>) {
unless (/$copyright/mi) {
s/($shebang)/$1\n$copyright/mig;
}
print;
}
使用perl ch9.pl sample_perl_script.pl
在命令行上运行。
我的目标是:
<>
。unless { ... }
)。 这适用于问题的第一部分(添加版权线),但不适用于第二部分(检查以确保版权尚不存在)。
我的问题是:为什么?为什么在运行程序时unless
完全被忽略了?
我偷看了附录,本书的建议解决方案是创建一个哈希来跟踪$ARGV
的文件名,并将文件传递两次。首先要删除已经有版权声明的文件,然后执行搜索/替换。像这样:
my %do_these;
foreach (@ARGV) {
$do_these{$_} = 1;
}
while (<>) {
if (/\A## Copyright/) {
delete $do_these{$ARGV};
}
}
@ARGV = sort keys %do_these;
$^I = ".bak";
while (<>) {
if (/\A#!/) {
$_ .= "## Copyright (c) 20XX by Yours Truly\n";
}
print;
}
这当然有效,但似乎是工作的两倍。我试图通过我的方法在单个while (<>) { ... }
循环中找到一种方法来做到这一点,并且更好地理解钻石操作符的工作原理。
如果我的方法完全偏离基础,请解释原因并且不要放过我的感受。我对比我的自我更全面的了解更感兴趣。
答案 0 :(得分:4)
你的书的方法 是愚蠢的。实际上,我认为perl正在bar,因为你的版权声明有(
之类的特殊字符。
你想要的是quotemeta
功能。 Link
我会改变你的程序:
while (<>) {
my $copyright2 = quotemeta $copyright;
unless (/$copyright2/mi) {
s/($shebang)/$1\n$copyright/mig;
}
print;
}
如果不起作用,请道歉。我写perl已经有一段时间了。
答案 1 :(得分:3)
您的unless
不起作用,因为版权与shebang不在同一行。菱形运算符读取一行直到$/
的第一个值,默认为换行符。您的程序将在不包含版权的所有行上执行替换。
由于这是perl,因此有很多方法可以解决它。最直接的方法可能是取消设置$/
并啜饮文件(将其全部读成一行)。这样,如果文件的第二行有版权声明,您可以立即查看。 E.g:
local $/; # slurp the file
while (<>) {
s/^.*\n\K(?!\Q$copyright\E)/$copyright/; # negative lookahead assertion
print;
}
您还可以直接在文件中检查第2行,而不会诋毁文件:
while (<>) {
if ($. == 2) {
unless (/\Q$copyright/) {
print "$copyright\n";
}
}
print;
close ARGV if eof; # this will reset the line counter $.
}
请注意,Nick ODell是正确的,您的版权字符串包含必须转义的元字符(即括号)。我在上面使用了\Q ... \E
转义序列。
另请注意,您不需要非常具体地检查shebang,这更容易让您在稍微变化的线路上绊倒。