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




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

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

入力:

hoho
hihi
haha
hede

コード:

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

望ましい出力:

hoho
hihi
haha

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

/hede|^(.*)$/gm

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


文字列全体が一致した場合にのみ正規表現テストを失敗させたい場合、次のように動作します:

^(?!hede$).*

例: - "foo"以外のすべての値を許可したい場合(つまり "foofoo"、 "barfoo"、 "foobar"は成功しますが "foo"は失敗します)、 ^(?!foo$).*

もちろん、 正確な平等をチェックしている場合、この場合のよりよい一般的な解決法は、文字列の等価性をチェックすることです。

myStr !== 'foo'

正規表現の機能が必要な場合(ここでは、大文字小文字の区別や範囲のマッチング)、テストの外側に否定を置くことさえできます:

!/^[a-f]oo$/i.test(myStr)

しかし、この答えの先頭にある正規表現の解決法は、(おそらくAPIによって)正の正規表現テストが必要な状況で役に立ちます。


FWIWは、正規言語(別名合理的言語)が補完によって閉じられるため、別の式を否定する正規表現(別名合理的表現)を見つけることは常に可能です。 しかし、これを実装するツールはあまりありません。

Vcsnはこの演算子をサポートしています( {c} 、postfixを示します)。

まず、式の型を定義します。ラベルは、 aからzまでを選択aための文字( lal_char )です(補完を扱うときにアルファベットを定義することはもちろん重要です)。そして、各単語について計算される "値"はただブール値です: trueは、単語が受け入れられ、 false 、拒否されます。

Pythonで:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 𝔹

式を入力します:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

この式をオートマトンに変換する:

In [7]: a = e.automaton(); a

最後に、このオートマトンを簡単な式に変換し直してください。

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

ここで、 +は通常、 | \eは空の単語を表し、 [^]は通常書き込まれ. (任意の文字)。 ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*

ではこの例を見ることができ、 there Vcsnをオンラインthere試してみることができます。


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

^(?!hede).*$

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

^((?!hede).)*$

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


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

^((?!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"が先にあります!)。


あなたがgrepのためだけに使っているのであればgrep -v hedeを使ってgrep -v hedeを含まないすべての行を得ることができます。

ETAああ、 grep -vはおそらくあなたが「ツールオプション」の意味で使っていたものです。


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"]

否定的な先読みでは、正規表現は特定のパターンを含まないものと一致する可能性があります。 これはBart Kiersによって答えられ説明されています。 素晴らしい説明!

しかし、Bart Kiersの答えでは、先読み部分は1文字にマッチして1〜4文字先をテストします。 これを避けて、先読み部分にテキスト全体をチェックさせ、「うん」がないことを確認してから、普通の部分(。*)がすべてのテキストを一度にすべて食べることができるようにします。

改良された正規表現は次のとおりです:

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

負のルックアヘッド部分の(*?)lazy量指定子はオプションであることに注意してください。データに応じてgreedy量指定子を代わりに使うことができます: 'hede'が存在し、テキストの最初の半分にlazy量指定子速くなる; さもなければ、欲張り量限定子はより速くなる。 しかし、 'hede'が存在しなければ、どちらも同じように遅くなります。

ここにデモコードがあります

先読みの詳細については、偉大な記事:ルックアヘッドとルックビハインドのマスターを参照してください。

また、複雑な正規表現の作成に役立つJavaScript正規表現ジェネレータRegexGen.jsご覧ください。 RegexGen.jsを使うと、より読みやすい方法で正規表現を構築することができます:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);

回答:

^((?!hede).)*$

説明:

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

あなたの文字列をhede

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


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

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

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

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

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


これにより、各ポジションの先読みテストを避けることができます。

/^(?:[^h]+|h++(?!ede))*+$/

に相当する(.net):

^(?>(?:[^h]+|h+(?!ede))*)$

古い答え:

/^(?>[^h]+|h+(?!ede))*$/

与えられた答えは完璧で、単なる学問的な点です:

理論的なコンピュータ科学の意味における正規表現は、これを好きではありません 。 彼らのためには、このようなものを見なければならなかった:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

これは、完全一致のみを行います。 サブマッチのためにそれをすることはさらに厄介なことになります。


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

たとえば、文字列:

<?
$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"

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

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


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

<?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;
    }


?>

私のやり方は次のとおりです:

^[^h]*(h(?!ede)[^h]*)*$

他の答えよりも正確で効率的です。 フリードルの「展開ループ」効率化手法を実装しバックトラッキングを大幅に削減します。


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

答えは、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?)?)?$

これは上記と同等です。


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).)*は固定できるのですばらしいです。

^(?:(?!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

正規表現ではありませんが、パイプを使ってシリアル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とのマッチ)


ではConyEditは、コマンドラインを使用することができますcc.gl !/hede/正規表現のマッチングが含まれている、またはコマンドラインを使用していない行を取得するためにcc.dl /hede/正規表現のマッチングを含む行を削除することを。彼らは同じ結果を持っています。







regex-group