regex - 複数 - 単語を含まない行と一致する正規表現ですか?




正規表現 完全一致 否定 (18)

ベンチマーク

私は提示されたオプションのいくつかを評価し、パフォーマンスを比較し、いくつかの新しい機能を使用することにしました。 .NET Regex Engineのベンチマーク: http://regexhero.net/tester/ : http://regexhero.net/tester/

ベンチマークテキスト:

検索された式が含まれているため、最初の7行は一致しないはずですが、下の7行は一致する必要があります。

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

結果:

結果は、3回の実行の中央値としての1秒あたりの反復数です - より大きい数値=より良い

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

.NETはアクション動詞(* FAILなど)をサポートしていないので、私はソリューションP1とP2をテストできませんでした。

概要:

私はほとんどの提案された解決策をテストしようとしました、いくつかの最適化は特定の単語に可能です。 たとえば、検索文字列の最初の2文字が同じでない場合、答え03は^(?>[^R]+|R+(?!egex Hero))*$してパフォーマンスを向上させることができます。

しかし、全体的に最も読みやすく、パフォーマンスに優れた最速のソリューションは、条件付きのステートメントを使用しているか、存在する量指定子で04になっているようです。 私は、Perlのソリューションはより速く、より簡単に読めるべきだと思います。

単語をマッチさせて、他のツール(例えばgrep -v )を使ってマッチを逆転させることが可能であることは分かっています。 しかし、正規表現を使って特定の単語(例えばhede)を含まない行をマッチさせることが可能かどうかを知りたいと思います。

入力:

hoho
hihi
haha
hede

コード:

grep "<Regex for 'doesn't contain hede'>" input

望ましい出力:

hoho
hihi
haha

PCREのバックトラック制御動詞を使用して単語が含まれていない行を照合する方法

これまで私が使っていなかった方法があります:

/.*hede(*COMMIT)^|/

使い方

まず、行のどこかに "hede"を見つけようとします。成功した場合は、この時点で(*COMMIT)、エンジンに、障害が発生した場合にバックトラックしないように指示するだけでなく、その場合にはさらにマッチングを試みないように指示します。次に、一致する可能性のあるもの(この場合は^)と一致するようにします。

行に "hede"が含まれていない場合は、空のサブパターンである第2の選択肢が問題の文字列と正常に一致します。

この方法は、否定的な先読みよりも効率的ではありませんが、誰かが気の利いたことを見つけて、他のより興味深いアプリケーションのために使いたいと思うように、


OPは、Regexが使用されるコンテキスト(プログラミング言語、エディタ、ツール)を示すために投稿を指定したりタグ付けしなかった。

私にとっては、 Textpadを使ってファイルを編集するときに、この作業を行う必要があることがあります。

TextpadはいくつかのRegexをサポートしていますが、先読みや見た目をサポートしていないので、いくつかのステップが必要です。

私が文字列を含んでいないすべての行を保持するために探しているなら、私は次のようにします:

1.ファイル全体を検索/置換して、テキストを含む各行の先頭に一意の「タグ」を追加します。

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2.文字列hedeを含むすべての行を削除します(置換文字列は空です)。

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3.この時点で、残りのすべての行には文字列hedeは含まれません 。 すべての行から一意の「タグ」を削除します(置換文字列は空です)。

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

今度は、削除された文字列を含むすべての行の元のテキストがあります。

もし私が文字列が含まれていない行だけに何かをしたいのであれば、私は次のようにします:

1.ファイル全体を検索/置換して、テキストを含む各行の先頭に一意の「タグ」を追加します。

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2.文字列hedeを含むすべての行について、一意の「タグ」を削除します。

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3.この時点で、一意の「タグ」で始まるすべての行に、文字列hedeは含まれません 。 私は今、それらの行だけに何かをすることができます。

4.完了したら、すべての行から一意の「タグ」を削除します(置換文字列は空です)。

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

ruby-2.4.1の導入以来、我々はRubyの正規表現で新しいAbsent Operatorを使うことができます

公式のdocから

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

したがって、あなたの場合、 ^(?~hede)$はあなたのために仕事をします

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

PCRE動詞(*SKIP)(*F)介して、

^hede$(*SKIP)(*F)|^.*$

これは、完全な文字列hedeを含む行を完全にスキップし、残りのすべての行と一致します。

DEMO

パーツの実行:

上記の正規表現を2つの部分に分割して考えてみましょう。

  1. |前の部分| シンボル。 パートを一致させないでください

    ^hede$(*SKIP)(*F)
    
  2. |後の部分| シンボル。 部分は一致する必要があります

    ^.*$
    

パート1

Regexエンジンは最初の部分から実行を開始します。

^hede$(*SKIP)(*F)

説明:

  • ^私達が最初にいると主張する。
  • hede文字列と一致するhede
  • $私たちが行末にいると主張する。

したがって、文字列を含む行は一致します。 正規表現エンジンが次の(*SKIP)(*F)注: (*FAIL)として )動詞として書くことができれば 、それはスキップして失敗します。 | PCRE動詞の隣に追加されたいわゆる変更または論理OR演算子は、すべての境界線に一致するすべての境界線に一致します。ただし、その行には正確な文字列が含まれています。 hereのデモを見てhere 。 つまり、残りの文字列の文字を一致させようとします。 今度は2番目の部分の正規表現が実行されます。

パート2

^.*$

説明:

  • ^私達が最初にいると主張する。 つまり、 hede行以外のすべての行開始に一致します。 hereのデモを見てhere
  • .*マルチラインモードでは、 . 改行文字またはキャリッジリターン文字を除く任意の文字と一致します。 そして*は前の文字を0回以上繰り返します。 だから、 .*は全体の行に一致します。 hereのデモを見てhere

    なぜあなたは。*の代わりに。*を追加したのでしょうか?

    .*は空白行と一致しますが、 .+は空白行と一致しません。 hede以外のすべての行に一致させたい場合、入力にも空白行が存在する可能性があります。 .+.+代わりに.+ .*を使用する必要があります。 .+は直前の文字を1回以上繰り返します。 .*は空白行とhere一致しhere

  • $ラインアンカーの終わりはここでは必要ありません。


回答:

^((?!hede).)*$

説明:

^文字列の始まり、 (グループとキャプチャ\ 1(0回以上(可能な限り多くの量にマッチする))、
(?!がないかどうかを先に見て、

あなたの文字列をhede

)ルックアヘッドの終わり. \ nを除く任意の文字、
)* end of \ 1(注:このキャプチャで量子を使用しているため、キャプチャされたパターンの最後の繰り返しのみが\ 1に格納されます)
オプションの\ nの前に$と文字列の最後


あなたが文字クラスを否定するのに似た単語を否定するために文字を照合したいならば:

たとえば、文字列:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

使ってはいけません:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

つかいます:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

通知"(?!bbb)." lookbehindでも先読みでもなく、lookcurrentです。たとえば、次のようになります。

"(?=abc)abcde", "(?!abc)abcde"

ここでは、任意の正規表現を否定することが簡単ではない理由の良い説明です。 私は他の答えに同意する必要があります:これは仮説的な質問以外のものであれば、正規表現はここで正しい選択ではありません。


への解決策は "hede" 始まらないことに注意してください。

^(?!hede).*$

一般的に"hede"を含まないソリューションよりもはるかに効率的です:

^((?!hede).)*$

前者は、すべての位置ではなく、入力文字列の最初の位置でのみ "hede"をチェックします。


上記(?:(?!hede).)*は固定できるのですばらしいです。

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

しかし、この場合は次のようにすれば十分です。

^(?!.*hede)                    # A line without hede

この単純化により、「AND」節を追加する準備が整います。

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

他の誰も質問された質問に直接答えを出していないので、私はそれをします。

答えは、POSIX grepでは、文字通りこの要求を満たすことは不可能です:

grep "Regex for doesn't contain hede" Input

その理由は、POSIX grep基本正規表現でのみ動作する必要があります。これは、そのタスクを達成するのに十分強力ではない(交替やグループ化がないため、通常の言語を解析できません)。

しかし、GNU grepはそれを可能にする拡張機能を実装しています。 特に、 \| GNUのBREの実装における交代演算子であり、 \(\)はグループ化演算子です。 あなたの正規表現エンジンが代替、否定的なブラケット式、グループ化、Kleeneの星をサポートし、文字列の先頭と末尾にアンカーすることができれば、この方法で必要なのはこれだけです。

GNU grepでは、次のようになります:

grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" Input

Grailといくつかのさらなる最適化が手で行われています)。

egrepような拡張正規表現を実装するツールを使用して、バックスラッシュを取り除くこともできます。

egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" Input

これをテストするスクリプトがあります(現在のディレクトリにtestinput.txtファイルが生成されます)。

#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"

# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede

h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)

私のシステムでは以下のように表示されます:

Files /dev/fd/63 and /dev/fd/62 are identical

予想通り。

詳細に関心のある人にとっては、単語にマッチする正規表現を有限オートマトンに変換し、すべてのアクセプタンス状態を非アクセプタンスに変更することによってオートマトンを反転させ、その結果のFAを正規表現

最後に、誰もが指摘しているように、正規表現エンジンが否定的な先読みをサポートしていると、タスクが大幅に単純化されます。 たとえば、GNU grepの場合:

grep -P '^((?!hede).)*$' Input

アップデート:私は最近、Kendall Hopkinsの優れたFormalTheoryライブラリを、Grailに似た機能を提供するPHPで書いています。 それを使用して、自分で書かれた簡略化を使用して、入力句(現在サポートされている英数字と空白文字のみ)を指定して、負の正規表現のオンラインジェネレータを書くことができました: http://www.formauri.es/personal/pgimeno/misc/non-match-regex/ : http://www.formauri.es/personal/pgimeno/misc/non-match-regex/

hedeためにそれは出力する:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

これは上記と同等です。


以下の機能は、あなたが望む出力を得るのに役立ちます

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

正規表現が逆マッチングをサポートしていないという考え方は完全に真実ではありません。 負のルックアラウンドを使用してこの動作を模倣することができます。

^((?!hede).)*$

上の正規表現は、(サブ)文字列 'hede'を含まない、任意の文字列、または改行なしの行に一致します。 前述したように、これは正規表現が「良い」(またはすべき)ものではありませんが、それでも可能です。

また、改行文字を一致させる必要がある場合は、 DOT-ALL修飾子を使用します(次のパターンの末尾にsを付けます)。

/^((?!hede).)*$/s

またはインラインで使用する:

/(?s)^((?!hede).)*$/

(ここで、/ /.../は正規表現の区切り文字、つまりパターンの一部ではありません)

DOT-ALL修飾子が利用できない場合、文字クラス[\s\S]同じ動作を模倣することができます。

/^((?!hede)[\s\S])*$/

説明

文字列はn文字のリストです。 各文字の前と後に空の文字列があります。 したがって、 n文字のリストには、 n n+1個の空の文字列があります。 文字列"ABhedeCD"考えてみましょう:

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

ここで、 eは空の文字列です。 正規表現(?!hede). 見える部分文字列がないかどうかを調べ、もしそうであれば(他の何かが見えるように) (ドット)は、改行以外の文字と一致します。 ルックアラウンドは、文字を消費しないため、 ゼロ幅アサーションとも呼ばれます 。 彼らは何かをアサート/検証するだけです。

だから、私の例では、すべての空の文字列が最初に検証され、キャラクタがキャラクタが使用される前に、先行する"hede"ないかどうかを確認します. (ドット)。 正規表現(?!hede). それは一度だけ行いますので、グループにまとめられ、0回以上繰り返されます: ((?!hede).)* 。 最後に、入力の開始と終了は、入力全体が消費されていることを確認するために固定されます。 ^((?!hede).)*$

ご覧のとおり、 e3では正規表現(?!hede)が失敗するため、入力"ABhedeCD"は失敗します( "hede"が先にあります!)。


正規表現ではありませんが、パイプを使ってシリアルgrepsを使用してノイズを除去することは論理的で便利です。

例えば。 すべてのコメントなしでApacheの設定ファイルを検索する -

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

そして

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

シリアルgrepのロジックはコメントではなく、(dirとのマッチ)


私はここで複雑な正規表現や先読みの必要性を理解していない:

/hede|^(.*)$/gm

あなたが欲しがらないものを捕捉グループに入れないでください。他のものには1つを使用してください。これは、 "hede"を含まないすべての行に一致します。


TXR言語は、正規表現の否定をサポートしています。

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

より複雑な例:部分文字列を含んでいない部分で始まる行aと終わりzの部分のすべての行に一致するhede

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

正規表現の否定はそれ自身では特に有用ではありませんが、交点を持つときには、ブール値セット操作の完全なセットがあるので興味深いものです:「これにマッチするものを除く」。


もっと簡単な解決策はnot演算子を使うことです!

あなたのifステートメントは "contains"にマッチし、 "excludes"にマッチしない必要があります。

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

RegExのデザイナーは、オペレーターではない人の使用を予期していたと思います。


コード内で2つのregexesを維持することができます.1つは最初の一致を行い、2つ目の正規表現を実行してブロックしたい外れ値の場合をチェック^.*(hede).*し、コードに適切なロジックを設定します。

これは本当に投稿された質問への回答ではなく、単一の正規表現よりもわずかに多くの処理を使用する可能性があることは認めています。しかし、アウトライアの場合の迅速な緊急修正を求めるためにここに来た開発者にとって、この解決策は見過ごされるべきではありません。





regex-group