4

Learning Perlの第 9 章「正規表現を使用したテキストの処理」に取り組んでいます。

以下は、章の終わりの演習の 2 つです。

  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

私の目標は次のとおりです。

  • パスに関係なく、元のシバンをそのまま保持します。
  • 1 回だけループし<>ます。
  • 著作権表示が存在するかどうかを確認します。
  • そうでない場合は、追加します(したがって、 で試行しunless { ... }ます)。

これは、問題の最初の部分 (著作権行の追加) では機能しますが、2 番目の部分 (著作権がまだ存在していないことを確認してください) では機能しません。

私の質問は次のとおりです。なぜですか?そしてunless、プログラムを実行すると完全に無視されるのはなぜですか?

付録をのぞき見したところ、この本の提案された解決策は、ファイル名を追跡するためのハッシュを作成し、ファイルを2 回$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;
}

もちろん、これは機能しますが、2 倍の作業のように思えます。私のアプローチを使用して、単一のwhile (<>) { ... }ループ内でこれを行う方法があるかどうかを確認し、ダイヤモンド演算子がどのように機能するかをよりよく理解できるようにしようとしています。

私のアプローチが完全に的外れである場合は、その理由を説明してください。私の気持ちを惜しまないでください。私は自分のエゴよりも完全な理解に関心があります。

4

2 に答える 2

4

あなたの本のアプローチばかげています。実際、あなたの著作権表示には(.

あなたが欲しいのは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著作権がシバンと同じ行にないため、あなたは機能しません。$/ひし形演算子は、 の最初の値 (デフォルトでは改行)まで行を読み取ります。プログラムは、著作権を含まないすべての行で置換を実行します。

これは perl なので、修正する方法はたくさんあります。最も簡単な方法は、おそらく$/ファイルの設定を解除して丸呑みすることです (すべてを 1 行に読み込みます)。そうすれば、ファイルの 2 行目に著作権表示があるかどうかをすぐに確認できます。例えば:

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上記のエスケープシーケンスを使用しました。

また、シバンをチェックする際に、それほど具体的にする必要はないことにも注意してください。

于 2013-01-14T17:09:59.450 に答える