2016-04-20 8 views
2

私は、コマンドラインでユーザからの入力として正規表現をとり、その後、現在のディレクトリの下にある特定のファイルを見つけるために、その正規表現を適用するアプリケーションに取り組んでいます。アプリケーションはUTF-8入力をサポートしており、UTF-8でエンコードされたファイル名を見つけることができます。File :: Find :: Ruleで使用する正規表現入力をユーザーがエンコードするのは安全ですか?

use feature qw(say); 
use open qw(:std :utf8); 
use strict; 
use utf8; 
use warnings; 

use Encode(); 
use File::Find::Rule; 

system 'touch', 'aæ', 'bæ', 'aa'; # some test files, 

my $pat = 'æ$'; 
my $pat_encode = encode($pat); 
run_test($pat_encode, 'With encode()'); 
run_test($pat, 'Without encode()'); 
my $pat2 = '[æ]$'; 
my $pat2_encode = encode($pat2); 
run_test($pat2_encode, 'With encode()'); 

sub encode { 
    return Encode::encode('UTF-8', $_[0], Encode::FB_CROAK | Encode::LEAVE_SRC); 
} 

sub run_test { 
    my ($pat_encode, $test_str) = @_; 

    say $test_str; 
    say '-' x length $test_str; 
    say ""; 
    my @files = File::Find::Rule->new->name(qr/$pat_encode/)->in('.'); 
    for (@files) { 
     $_ = Encode::decode('UTF-8', $_, Encode::FB_CROAK | Encode::LEAVE_SRC); 
    } 

    say $_ for @files; 
} 

出力は次のとおりです:ここでは一例である

With encode() 
------------- 

aæ 
bæ 
Without encode() 
---------------- 

With encode() 
------------- 

aæ 
bæ 
私は最後の正規表現 [æ]$æは2バイト 0xC3A6に展開されるので、符号化された後に動作しますが、何とかしないことを期待する

Perlは、正規表現がUTF-8でエンコードされていることを知っているようで、動作するためにはいくつかの魔法を使っているようです。

後者の例が動作している、そして他の例がある場合は正規表現をコードする機能しない理由を誰かが知っているのだろうか? (File::Find::Ruleを使用できるかどうか、またはFile::Findに切り替える必要があるかどうかを判断しようとしています)

答えて

3

正規表現をエンコードすることは安全ではないことが判明しています。特に、ブラケット式の後に1つ以上の文字が続く場合、正規表現は不要なファイルを選択する可能性があります。その理由は、UTF-8でエンコードされたバージョンのバイトのうちの1つだけが括弧の式によってマッチされるからです。しかし$pat2正規表現もでアップに使用されるエンコードæの2バイトの最初以来を返します、

system 'touch', 'aæ', 'aæ1', 'aa'; # some test files, 

my $pat = 'æ.$'; 
my $pat_encode = encode($pat); 
run_test($pat_encode, 'With encode()'); 
run_test($pat, 'Without encode()'); 
my $pat2 = '[æ].$'; 
my $pat2_encode = encode($pat2); 
run_test($pat2_encode, 'With encode()'); 

は今、これが唯一のファイルaæ1を返す必要があります:私のスクリプトの以下の変更を考えてみましょう最後のバイトを残しブラケット表現は、$pat2.を末尾にマッチしたことにします。

出力は次のようになります。

With encode() 
------------- 

aæ1 
Without encode() 
---------------- 

With encode() 
------------- 

aæ 
aæ1 

ソリューションは、代わりにFile::Findを使用するように思わ:

use File::Find(); 

system 'touch', 'aæ', 'aæ1', 'aa'; # some test files, 

my $pat = '[æ].$'; 
my $files = find_files($pat); 

say $_ for @$files; 

sub decode { 
    return Encode::decode('UTF-8', $_[0], Encode::FB_CROAK | Encode::LEAVE_SRC); 
} 

sub find_files { 
    my ($pat) = @_; 

    my @files; 
    File::Find::find(sub { wanted($pat, \@files) }, '.'); 
    return \@files; 
} 

sub wanted { 
    my ($pat, $files) = @_; 
    my $name = decode($_); 
    my $full_name = decode($File::Find::name); 
    push @$files, $full_name if $name =~ /$pat/; 
} 

出力され、今正しい:

./aæ1 

更新

実際に

File::Find::Ruleは、すべての後に使用することができます。出力は今

my $pat = '[æ].$'; 
my $files = find_files($pat); 

say for @$files; 

sub find_files { 
    my ($pat) = @_; 

    my @files = File::Find::Rule->new->exec(sub { wanted($pat) })->in('.'); 
    for (@files) { 
     $_ = decode($_); 
    } 
    return \@files; 
} 

sub wanted { 
    my ($pat) = @_; 
    my $name = decode($_); 
    return ($name =~ /$pat/) ? 1 : 0; 
} 

aæ1 
単に代わり nameルールの execルールを使用
関連する問題