5

私は最近 Perl の学習を始めました。私の最近の課題の 1 つは、特定の文字列を見つけるために多数のファイルを検索することです。ユーザーがディレクトリ名を引数として指定すると、プログラムはそのディレクトリ内のすべてのファイルでパターンを検索します。を使用readdir()して、検索可能なすべてのファイル名を含む配列を作成できたので、すべてのファイルでパターンを検索する必要があります。私の実装は次のようになります-

sub searchDir($) {
    my $dirN = shift;
    my @dirList = glob("$dirN/*");
    for(@dirList) {
        push @fileList, $_ if -f $_;

    }
    @ARGV = @fileList;
    while(<>) {
        ## Search for pattern
    }
}

私の質問は、上記のように @ARGV 配列を手動でロードし、 <> 演算子を使用して個々の行をスキャンしても大丈夫ですか、それとも各ファイルを個別に開いたり、スキャンしたり、閉じたりする必要がありますか? この処理がメイン関数ではなくサブルーチンにある場合、違いはありますか?

4

5 に答える 5

3

私はこのより明示的で読みやすいバージョンを好みます:

#!/usr/bin/perl -w 

foreach my $file (<$ARGV[0]/*>){
    open(F, $file) or die "$!: $file";
    while(<F>){
      # search for pattern
    }
    close F;
}

しかし、次のように操作しても問題ありません@ARGV

#!/usr/bin/perl -w 

@ARGV = <$ARGV[0]/*>;
while(<>){
    # search for pattern
}
于 2009-02-03T04:37:49.207 に答える
1

前の回答は、Perl プログラミングに関する主な質問をかなりうまくカバーしています。

それでは、根底にある質問についてコメントさせてください。ファイルの束からパターンを見つける方法です。

OS によっては、特殊な外部プログラムを呼び出すのが理にかなっている場合があります。

grep -l <pattern> <path>

ユニックスで。

パターンを含むファイルで何をする必要があるか、およびヒット/ミスの比率がどの程度かにもよりますが、これによりかなりの時間を節約できます (そして実証済みのコードを再利用します)。

于 2009-02-03T10:24:20.803 に答える
1

はい、' while (<>)' ループを開始する前に引数リストを調整しても問題ありません。ループ内で調整するのは無謀です。たとえば、オプション引数を処理する場合、通常は @ARGV からアイテムを削除します。ここでは項目を追加していますが、それでも @ARGV の元の値が変更されます。

コードがサブルーチンにあるか「メイン関数」にあるかは関係ありません。

于 2009-02-03T04:36:21.177 に答える
0

@ARGVを微調整する際の大きな問題は、それがグローバル変数であるということです。while (<>)また、には特別な魔法の属性があることに注意する必要があります。(各ファイルを読み込んだり、空の場合は@ARGV処理したりして、真実ではなく定義をテストします)。理解する必要のある魔法を減らすために、quickie-hack-jobsを除いて、私はそれを避けます。STDIN@ARGV

をチェックすると、現在のファイルのファイル名を取得できます$ARGV

気付いていないかもしれませんが、実際には、だけでなく2つのグローバル変数に影響を与えています@ARGV。あなたも打って$_います。ローカライズすることも非常に良い考え$_です。

localを使用して変更をローカライズする ことにより、グローバル変数の変更による影響を減らすことができます。

ところで、。にはもう1つの重要で微妙な魔法があり<>ます。一致する行番号をファイルに返したいとします。perlvarをチェック$.して、最後にアクセスしたハンドルの行番号が表示されると思うかもしれません。しかし、ここに潜んでいる問題があります-ファイル$.間でリセットされません。@ARGVこれは、処理した合計行数を知りたい場合に最適ですが、現在のファイルの行番号が必要な場合は役立ちません。eof幸いなことに、この問題を解決する 簡単なトリックがあります。

use strict;
use warnings;

...

searchDir( 'foo' );

sub searchDir {
    my $dirN    = shift;
    my $pattern = shift;

    local $_;

    my @fileList = grep { -f $_ } glob("$dirN/*");

    return unless @fileList;  # Don't want to process STDIN.

    local @ARGV;

    @ARGV = @fileList;
    while(<>) {
        my $found = 0;
        ## Search for pattern
        if ( $found ) {
            print "Match at $. in $ARGV\n";
        }
    }
    continue {
        # reset line numbering after each file.
        close ARGV  if eof;  # don't use eof().
    }
}

警告:ブラウザでコードを変更しました。私はそれを実行していないので、タイプミスがある可能性があり、少し調整しないと機能しない可能性があります

更新local代わりに使用する理由myは、それらが非常に異なることを行うためです。 含まれているブロックにのみ表示され、シンボルテーブルからアクセスできない my新しい字句変数を作成します。既存のパッケージ変数localを保存し、それを新しい変数にエイリアスします。新しいローカライズされたバージョンは、囲んでいるブロックを離れるまで、後続のコードに表示されます。perlsub:local()による一時的な値を参照してください。

新しい変数を作成して使用する一般的なケースでmyは、が正しい選択です。 localグローバルを使用している場合は適切ですが、変更をプログラムの残りの部分に伝播しないようにする必要があります。

この短いスクリプトは、ローカルを示しています。

$foo = 'foo';

print_foo();
print_bar();
print_foo();

sub print_bar {
    local $foo;
    $foo = 'bar';
    print_foo();
}

sub print_foo {
    print "Foo: $foo\n";
}
于 2009-02-04T05:18:12.480 に答える