web-dev-qa-db-ja.com

Perl文字列パターンマッチの負の正規表現

私はこの正規表現を持っています:

if($string =~ m/^(Clinton|[^Bush]|Reagan)/i)
  {print "$string\n"};

私はクリントンとレーガンにマッチしたいが、ブッシュにはマッチしたくない。

動いていない。

43
joe

サンプルテキスト:

クリントンは言った
ブッシュはクレヨンを使用しました
レーガンは忘れました

ブッシュマッチを省略するだけです:

$ Perl -ne 'print if /^(Clinton|Reagan)/' textfile
Clinton said
Reagan forgot

または、本当に指定したい場合:

$ Perl -ne 'print if /^(?!Bush)(Clinton|Reagan)/' textfile
Clinton said
Reagan forgot
27
Demosthenex

[]は文字クラスを定義しているため正規表現は機能しませんが、必要なのは先読みです:

(?=) - Positive look ahead assertion foo(?=bar) matches foo when followed by bar
(?!) - Negative look ahead assertion foo(?!bar) matches foo when not followed by bar
(?<=) - Positive look behind assertion (?<=foo)bar matches bar when preceded by foo
(?<!) - Negative look behind assertion (?<!foo)bar matches bar when NOT preceded by foo
(?>) - Once-only subpatterns (?>\d+)bar Performance enhancing when bar not present
(?(x)) - Conditional subpatterns
(?(3)foo|fu)bar - Matches foo if 3rd subpattern has matched, fu if not
(?#) - Comment (?# Pattern does x y or z)

だから試してください:(?!bush)

132
Stuck

あなたの正規表現は次のように言っています:

/^         - if the line starts with
(          - start a capture group
Clinton|   - "Clinton" 
|          - or
[^Bush]    - Any single character except "B", "u", "s" or "h"
|          - or
Reagan)   - "Reagan". End capture group.
/i         - Make matches case-insensitive 

つまり、言い換えれば、正規表現の中間部分はあなたを台無しにしています。これは「キャッチオール」グループの一種であるため、「ブッシュ」の大文字または小文字のいずれでも始まらない行を許可します。たとえば、次の行は正規表現に一致します。

Our president, George Bush
In the news today, pigs can fly
012-3123 33

前述のように、ネガティブな先読みを行うか、単に2つの正規表現を作成します。

if( ($string =~ m/^(Clinton|Reagan)/i) and
    ($string !~ m/^Bush/i) ) {
   print "$string\n";
}

mirod がコメントで指摘しているように、キャレット(^)「クリントン」または「レーガン」で始まる行が「ブッシュ」で始まることはないため、行の先頭のみに一致します。

ただし、キャレットがなくても有効です。

17
TLP

2つの正規表現(または3つ)の使用の何が問題になっていますか?これにより、意図がより明確になり、パフォーマンスが向上する場合があります。

if ($string =~ /^(Clinton|Reagan)/i && $string !~ /Bush/i) { ... }

if (($string =~ /^Clinton/i || $string =~ /^Reagan/i)
        && $string !~ /Bush/i) {
    print "$string\n"
}
3
mob

私の理解が正しければ、クリントンとレーガンを含む任意の行に一致しますが、ブッシュには一致しません。 Stuckが提案したように、先読みアサーション付きのバージョンは次のとおりです。

#!/usr/bin/Perl

use strict;
use warnings;

my $regex = qr/
    (?=.*clinton)  
    (?!.*bush) 
    .*reagan       
    /ix;

while (<DATA>) {
    chomp;
    next unless (/$regex/);
    print $_, "\n";
}


__DATA__
shouldn't match - reagan came first, then clinton, finally bush
first match - first two: reagan and clinton
second match - first two reverse: clinton and reagan
shouldn't match - last two: clinton and bush
shouldn't match - reverse: bush and clinton
shouldn't match - and then came obama, along comes mary
shouldn't match - to clinton with Perl

結果

first match - first two: reagan and clinton
second match - first two reverse: clinton and reagan

必要に応じて、レーガンとクリントンを任意の順序で含む任意の行に一致します。

http://www252.pair.com/comdog/mastering_Perl/Chapters/02.advanced_regular_expressions.html の例で先読みアサーションがどのように機能するかを読んでみてください。

彼らはとてもおいしいです:)

2
Ashish Kumar