正規表現:先読みなしの除外によるマッチング-可能ですか?
-
19-08-2019 - |
質問
一部の正規表現フレーバーでは、[負の]ゼロ幅アサーション(先読み/後読み)はサポートされていません。
これにより、除外を記載することが非常に難しくなります(不可能ですか?)。たとえば、<!> quot; にないすべての行には<!> quot; foo <!> quot;その上で<!> quot ;、このように:
^((?!foo).)*$
ルックアラウンドをまったく使用せずに同じことを達成できますか(現時点では、複雑さとパフォーマンスの問題はさておき)
解決
更新: <!> quot; oo <!> quotの前に2つのffがある場合、失敗します。 @Cianticがコメントで指摘したように。
^(f(o[^o]|[^o])|[^f])*$
注:上記の正規表現を使用する代わりに、クライアント側で一致を否定する方がはるかに簡単です。
正規表現は、C ++およびgrepの正規表現が表示されない場合、各行が改行文字で終わると想定します。
Perl、Python、C ++、およびgrep
のサンプルプログラムはすべて同じ出力を提供します。
-
#!/usr/bin/perl -wn print if /^(f(o[^o]|[^o])|[^f])*$/;
-
#!/usr/bin/env python import fileinput, re, sys from itertools import ifilter re_not_foo = re.compile(r"^(f(o[^o]|[^o])|[^f])*$") for line in ifilter(re_not_foo.match, fileinput.input()): sys.stdout.write(line)
-
c ++
#include <iostream> #include <string> #include <boost/regex.hpp> int main() { boost::regex re("^(f(o([^o]|$)|([^o]|$))|[^f])*$"); //NOTE: "|$"s are there due to `getline()` strips newline char std::string line; while (std::getline(std::cin, line)) if (boost::regex_match(line, re)) std::cout << line << std::endl; }
-
$ grep "^\(f\(o\([^o]\|$\)\|\([^o]\|$\)\)\|[^f]\)*$" in.txt
サンプルファイル:
foo
'foo'
abdfoode
abdfode
abdfde
abcde
f
fo
foo
fooo
ofooa
ofo
ofoo
出力:
abdfode
abdfde
abcde
f
fo
ofo
他のヒント
この質問に出くわし、完全に機能する正規表現が存在しないという事実を個人的な挑戦として受け止めました。すべての入力に対して機能する正規表現を作成できたと思います-原子グループ / 所有数量詞
もちろん、アトミックグループ化を許可するがルックアラウンドを許可しないフレーバーがあるかどうかはわかりませんが、質問では、正規表現でルックアラウンドなしで除外を指定できるかどうかが尋ねられ、< em> は技術的に可能です:
\A(?:$|[^f]++|f++(?:[^o]|$)|(?:f++o)*+(?:[^o]|$))*\Z
説明:
\A #Start of string
(?: #Non-capturing group
$ #Consume end-of-line. We're not in foo-mode.
|[^f]++ #Consume every non-'f'. We're not in foo-mode.
|f++(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, but only exit foo-mode if 'o' is not the next character. Thus, 'f' is valid but 'fo' is invalid.
|(?:f++o)*+(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, followed by a single 'o'. Repeat, since '(f+o)*' by itself cannot contain 'foo'. Only exit foo-mode if 'o' is not the next character following (f+o). Thus, 'fo' is valid but 'foo' is invalid.
)* #Repeat the non-capturing group
\Z #End of string. Note that this regex only works in flavours that can match $\Z
何らかの理由でアトミックグループ化を使用できるが、所有量限定子もルックアラウンドも使用できない場合は、次を使用できます。
\A(?:$|(?>[^f]+)|(?>f+)(?:[^o]|$)|(?>(?:(?>f+)o)*)(?:[^o]|$))*\Z
しかし、他の人が指摘しているように、他の方法でマッチを否定する方がおそらく実用的です。
通常、fooを探して、クライアントコードから正規表現の一致の結果を反転できます。
簡単な例として、文字列に特定の文字のみが含まれていることを検証したいとします。
このように書くことができます:
^[A-Za-z0-9.$-]*$
有効なtrue
結果を受け入れるか、次のようにします:
[^A-Za-z0-9.$-]
有効なfalse
結果を受け入れます。
もちろん、これは常にオプションではありません。たとえば、式を設定ファイルに入れるか、たとえば別のプログラムに渡す必要がある場合があります。しかし、覚えておく価値はあります。 たとえば、このような否定を使用できる場合、式は much より簡単になります。
この質問に出くわして、自分の正規表現除外ソリューションを探しました。ここでは、正規表現内でシーケンスを除外しようとしています。
この状況に対する私の最初の反応:たとえば、<!> quot; <!> quot; foo <!> quotを持たないすべての行。その上で!
これは、「foo」と一致しないファイル内のすべての行を返します 非常に単純なので、あなたの質問を読み間違えただけの強い気分になります。...grep -v foo