4

我正在学习 Perl的第 9 章“使用正则表达式处理文本”。

以下是两章结尾的练习:

  1. 编写一个程序,为到目前为止的所有练习答案添加版权行,## Copyright (c) 20XX by Yours Truly在文件中的“shebang”行之后立即放置一行。假设程序将被调用,文件名已经在命令行上进行编辑。

  2. 修改之前的程序,使其不编辑已经包含版权行的文件。作为对此的提示,您可能需要知道菱形运算符正在读取的文件的名称在 $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.

我的目标是:

  • 无论路径如何,都保持原始 shebang 完整。
  • 只循环<>一次。
  • 检查版权声明是否存在。
  • 如果没有,请添加它(因此尝试使用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 (<>) { ... }循环中执行此操作,并更好地了解菱形运算符的工作原理。

如果我的方法完全偏离基础,请解释原因,不要吝啬我的感受。我对完全理解比我的自我更感兴趣。

4

2 回答 2

4

你这本书的方法愚蠢。实际上,我认为 perl 令人讨厌,因为您的版权声明有特殊字符,例如(.

你想要的是quotemeta功能。关联

我会像这样更改您的程序:

while (<>) {
    my $copyright2 = quotemeta $copyright;
    unless (/$copyright2/mi) {
        s/($shebang)/$1\n$copyright/mig;
    }
    print;
}

抱歉,如果这不起作用。自从我写 perl 以来已经有一段时间了。

于 2013-01-14T16:24:50.773 回答
3

您的unless不起作用,因为版权与shebang不在同一行。菱形运算符读取一行直到 的第一个值$/,默认情况下是换行符。您的程序将对所有不包含版权的行执行替换。

由于这是 perl,因此有很多方法可以修复它。最直接的方法可能是取消设置$/和 slurp 文件(将其全部读入一行)。这样您就可以立即检查文件的第二行是否有版权声明。例如:

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,这更有可能让您在稍微不同的线路上绊倒。

于 2013-01-14T17:09:59.450 回答