web-dev-qa-db-ja.com

正規表現で特定のWordを否定するには?

私は[^bar]のように文字のグループを否定できることを知っていますが、否定が特定のWordに適用される正規表現が必要です - それで私の例では"bar"ではなく実際の"any chars in bar"を否定しますか?

537
Bostone

これを行うための素晴らしい方法は、 ネガティブルックアヘッド を使用することです。

^(?!.*bar).*$

負の先読み構造は、左括弧の後に疑問符と感嘆符が続く、括弧のペアです。先読みの内側[任意の正規表現パターン]です。

594

パフォーマンスが最大の関心事でない限り、否定したい単語に一致するものをスキップして、2回目のパスを実行するだけの方が簡単なことがよくあります。

正規表現は、通常、スクリプト作成や何らかの低パフォーマンスのタスクを実行していることを意味するため、読みやすく、理解しやすく、保守しやすいソリューションを探しましょう。

59
Bryan Oakley

次の正規表現はあなたが望むことをするでしょう(負の先読みと先読みがサポートされている限り)。唯一の問題はそれが個々の文字と一致するということです(すなわち、それぞれの一致は2つの連続した "バー"の間のすべての文字ではなく単一文字です)。

b(?!ar)|(?<!b)a|a(?!r)|(?<!ba)r|[^bar]
43
JAB

負の先読みまたは先読み のいずれかを使用できます。

^(?!.*?bar).*
^(.(?<!bar))*?$

あるいは単に基本を使う:

^(?:[^b]+|b(?:$|[^a]|a(?:$|[^r])))*$

これらはすべてbarを含まないものすべてに一致します。

41
Gumbo

次の英語の文の正規表現を識別しようとしているときに、このフォーラムのスレッドに出くわしました。

入力文字列が与えられた場合、 everything exceptと一致させます。この入力文字列は正確に 'bar'です。例えば、私は 'barrier'と 'disbar'、そして 'foo'とマッチさせたいのです。

これが私が思いついた正規表現です。

^(bar.+|(?!bar).*)$

正規表現の私の英語の翻訳は "それが 'bar'で始まっていて少なくとも1つの他の文字がある場合、または文字列が 'bar'で始まっていない場合に一致します.

29

解決策:

^(?!.*STRING1|.*STRING2|.*STRING3).*$

xxxxxx _ ok _

xxxSTRING1xxx KO(必要かどうか)

xxxSTRING2xxx KO(必要かどうか)

xxxSTRING3xxx KO(必要かどうか)

28
sgrillon

受け入れられている答えはNiceですが、実際には正規表現に単純な部分式否定演算子がないための回避策です。これがgrep --invert-matchが終了する理由です。そのため、* nixesでは、パイプと2番目の正規表現を使用して目的の結果を達成できます。

grep 'something I want' | grep --invert-match 'but not these ones'

それでも回避策はありますが、覚えやすいかもしれません。

8
Greg Bell

私は受け入れられた答えを補完し、私の遅い答えで議論に貢献したいと思います。

@ChrisVanOpstal共有 この正規表現のチュートリアル は正規表現を学ぶための素晴らしいリソースです。

しかし、読むのには本当に時間がかかりました。

私はニーモニックの便宜のためにチートシートを作りました。

この参照は、各クラスの先頭にある[]()、および{}の中括弧に基づいています。思い出しやすいのです。

Regex = {
 'single_character': ['[]', '.', {'negate':'^'}],
 'capturing_group' : ['()', '|', '\\', 'backreferences and named group'],
 'repetition'      : ['{}', '*', '+', '?', 'greedy v.s. lazy'],
 'anchor'          : ['^', '\b', '$'],
 'non_printable'   : ['\n', '\t', '\r', '\f', '\v'],
 'shorthand'       : ['\d', '\w', '\s'],
 }
3
JawSaw

私はファイル名のリストを持っていました、そして私はこの種のふるまいで特定のものを除外したかったです(Ruby):

files = [
  'mydir/states.rb',      # don't match these
  'countries.rb',
  'mydir/states_bkp.rb',  # match these
  'mydir/city_states.rb' 
]
excluded = ['states', 'countries']

# set my_rgx here

result = WankyAPI.filter(files, my_rgx)  # I didn't write WankyAPI...
assert result == ['mydir/city_states.rb', 'mydir/states_bkp.rb']

これが私の解決策です:

excluded_rgx = excluded.map{|e| e+'\.'}.join('|')
my_rgx = /(^|\/)((?!#{excluded_rgx})[^\.\/]*)\.rb$/

このアプリケーションの私の仮定:

  • 除外する文字列は入力の先頭、またはスラッシュの直後にあります。
  • 許可された文字列は.rbで終わります。
  • 許可されたファイル名は.の前に.rb文字を持っていません。
1

他に何かできることを考えてください。正規表現を使用していないため、最初の回答とは大きく異なるため、2番目の回答を投稿することにしました。

分割対象の引数として否定するには、Wordの文字列に対して同等の選択言語のsplit()メソッドを使用します。 Pythonを使った例

>>> text = 'barbarasdbarbar 1234egb ar bar32 sdfbaraadf'
>>> text.split('bar')
['', '', 'asd', '', ' 1234egb ar ', '32 sdf', 'aadf']

少なくともPythonでは、このようにすることのいいところ(機能がVisual BasicとJavaのどちらでも同じであるかどうか覚えていません)、「bar」がいつ繰り返されたかを間接的に知ることができます。 "bar"の間の空の文字列が結果のリストに含まれているという事実に起因する文字列(ただし、先頭の空の文字列は文字列の先頭に "bar"があるためです)。そうしたくない場合は、空の文字列をリストから削除するだけです。

1
JAB

このコメントから抽出 / bkDJ

^(?!bar$).*

このソリューションのNiceプロパティは、複数の単語を明確に否定(除外)することが可能であるということです。

^(?!bar$|foo$|banana$).*
0
leventov