2016-10-09 9 views
1

ファイルフィルターの場合、単語の配列を使用したいと思います。私はすでにこれにかなり単純なアプローチ(のみ不可欠一致する部分)が Perl、配列の項目の検索文字列

# check if any of the @words is found in $term 

@words= qw/one 
two 
three/; 
$term= "too for the show"; 

# the following looks very C like 

$size= @words; 
$found= 0; 

for ($i= 0; $i<$size && !$found; $i++) { 
    $found|= $term=~ /$words[$i]/; 
} 

printf "found= %d\n", $found; 

はPerlで難解な構文とソリューションをたくさん見たが、私は思ったんだけどあれば(というか何を)これを書くよりコンパクトな方法です。

答えて

3

は、すべての単語から正規表現を作成し、ちょうど1試合行います

/\b(?:$regex)\b/マッチング
#!/usr/bin/perl 
use warnings; 
use strict; 

my @words = qw(one two three); 

my $regex = join '|', map quotemeta, @words; 

for my $term ('too for the show', 'five four three', 'bones') { 
    my $found = $term =~ $regex; 
    printf "found = %d\n", $found; 
} 

oneに一致するからbonesを防止するであろうが。

+0

[データから 'list2re' ::のmunge](https://metacpan.org/pod/Data::Munge#list2re-LISTは)非常によく似た何かをするだけでなく、いくつかのエッジケースを処理します。 OPからの – melpomene

+0

+1。私はこれが好きです(それは私が期待していたperlのアラカンの方法を示しています)。しかし、アセンブリを持っている人は私のニーズをより良く適合させます。回答ありがとうございます。 – Terminality

2

Regexp::Assembleを使用して検索を1つの正規表現に変換します。こうすることで、各文字列を一度スキャンするだけで済むので、多数の行に対して効率的になります。

Regexp :: Assembleは手動で行うよりも好ましい方法です。そのような正規表現を使ってやりたいことがあるかもしれないことの完全なAPIを持っていますし、エッジケースを扱うことができ、より効率的な正規表現にインテリジェントにコンパイルすることができます。

たとえば、このプログラムでは(?^:\b(?:t(?:hree|wo)|one)\b)が生成され、バックトラッキングが少なくなります。単語リストのサイズが大きくなると、これは非常に重要になります。最近のバージョンのPerlは、5.14以上でこれを行います。

use strict; 
use warnings; 
use v5.10; 

use Regexp::Assemble; 

# Wrap each word in \b (word break) so only the full word is 
# matched. 'one' will match 'money' but '\bone\b' won't. 
my @words= qw(
    \bone\b 
    \btwo\b 
    \bthree\b 
); 

# These lines simulate reading from a file. 
my @lines = (
    "won for the money\n", 
    "two for the show\n", 
    "three to get ready\n", 
    "now go cat go!\n" 
); 

# Assemble all the words into one regex. 
my $ra = Regexp::Assemble->new; 
$ra->add(@words); 

for my $line (@lines) { 
    print $line if $line =~ $ra; 
} 

はまたforeach style loop to iterate over an array、およびstatement modifierの使用を注意してください。

最後に、\bを使用して、実際の単語のみが一致するようにしました。部分文字列ではなくmoneyです。

+0

最近のバージョンのperlは 'one | two | three'を内部的にトライにコンパイルしますが、これはバックトラッキングを行いません。 – melpomene

+0

@melpomeneええ、それは言及する価値がある。ありがとう。これは非常にいい最適化ですが、Perlのバージョンに効率的な依存性があります(私は[これは5.14について安定したと言いたい](http://perldoc.perl.org/5.14.0/perldelta.html#Regular- Expression-Bug-Fixes)?)そして、私は扱うことができる正規表現がどれほど複雑かは分かりません。私はパフォーマンスが正規表現の最適化に大きく依存しているものには依存しません。そしてRegexp :: Assembleは他の多くの問題を解決し、それはまだ価値があります。 – Schwern

+0

私はすでに他のものには5.10+を必要としているので、それは私にはあまり問題ではありません。 – melpomene

2

これはおそらくCのようなコードをperlに単純すぎる "翻訳"です。

  • プロ:それは非常に効率的ではないのです(他の回答が良く、ここでトンです):それはコンパクト
  • コンです。
@words= qw/one 
two 
three/; 
$term= "too for the show"; 

my @found = grep { $term =~ /$_/; } @words; 

printf "found= %d\n", scalar @found; 
+1

カウントが必要な場合は、 'my $ count = grep {$ term =〜/ $ _ /} @ words'も動作します。 OPからの – melpomene

+0

+1。私はこれが好きです(それは私が期待していたperlのアラカンの方法を示しています)。しかし、アセンブリを持っている人は私のニーズをより良く適合させます。回答ありがとうございます。 – Terminality

関連する問題