web-dev-qa-db-ja.com

セミコロンで終了したC ++ forおよびwhileループを検出するための正規表現

私のPythonアプリケーションでは、セミコロン(;)で終了したC++ forまたはwhileループに一致する正規表現を記述する必要があります。たとえば、次のように一致する必要があります。

for (int i = 0; i < 10; i++);

...しかし、これではありません:

for (int i = 0; i < 10; i++)

これは、開始かっこと閉じかっこの間のテキストに他のかっこが含まれている可能性があることに気付くまで、一見簡単に見えます。次に例を示します。

for (int i = funcA(); i < funcB(); i++);

Python.reモジュールを使用しています。現在、私の正規表現は次のようになっています(コメントを簡単に理解できるように、コメントを残しています)。

# match any line that begins with a "for" or "while" statement:
^\s*(for|while)\s*
\(  # match the initial opening parenthesis
    # Now make a named group 'balanced' which matches a balanced substring.
    (?P<balanced>
        # A balanced substring is either something that is not a parenthesis:
        [^()]
        | # …or a parenthesised string:
        \( # A parenthesised string begins with an opening parenthesis
            (?P=balanced)* # …followed by a sequence of balanced substrings
        \) # …and ends with a closing parenthesis
    )*  # Look for a sequence of balanced substrings
\)  # Finally, the outer closing parenthesis.
# must end with a semi-colon to match:
\s*;\s*

これは上記のすべての場合に完全に機能しますが、forループの3番目の部分に関数を含めるようにするとすぐに壊れます。

for (int i = 0; i < 10; doSomethingTo(i));

開き括弧と閉じ括弧の間にテキストを入れるとすぐに、「バランスのとれた」グループが含まれているテキストと一致するため、一致しないため(?P=balanced)部分が機能しなくなるからです。括弧内のテキストが異なるという事実)。

Pythonコードでは、VERBOSEフラグとMULTILINEフラグを使用し、次のような正規表現を作成しています。

REGEX_STR = r"""# match any line that begins with a "for" or "while" statement:
^\s*(for|while)\s*
\(  # match the initial opening parenthesis
    # Now make a named group 'balanced' which matches
    # a balanced substring.
    (?P<balanced>
        # A balanced substring is either something that is not a parenthesis:
        [^()]
        | # …or a parenthesised string:
        \( # A parenthesised string begins with an opening parenthesis
            (?P=balanced)* # …followed by a sequence of balanced substrings
        \) # …and ends with a closing parenthesis
    )*  # Look for a sequence of balanced substrings
\)  # Finally, the outer closing parenthesis.
# must end with a semi-colon to match:
\s*;\s*"""

REGEX_OBJ = re.compile(REGEX_STR, re.MULTILINE| re.VERBOSE)

誰でもこの正規表現の改善を提案できますか?頭を動かすのは複雑すぎます。

34
Thomi

正規表現を使用せずに、これを実行する非常に簡単な小さなルーチンを作成できます。

  • posまたはforの後の開始括弧の直前を指すように、位置カウンターwhileを設定します。
  • オープンブラケットカウンターopenBrを_0_に設定します。
  • ここで、posをインクリメントし続け、それぞれの位置の文字を読み取り、開き角かっこが表示されたらopenBrをインクリメントし、閉じ角かっこが表示されたらデクリメントします。これは、最初に「_for (_」の最初の開始ブラケットに対して1つずつ増分し、その間のいくつかのブラケットに対してさらに増分および減分し、forブラケットが閉じるときに_0_に戻します。
  • したがって、openBrが再び_0_になったら停止します。

停止位置は、for(...)の閉じ括弧です。これで、セミコロンが続いているかどうかを確認できます。

108
Frank

これは、正規表現で実際に行うべきではない種類のことです。文字列を一度に1文字ずつ解析し、開き括弧と閉じ括弧を追跡します。

探しているのがこれだけであれば、本格的なC++文法レクサー/パーサーは必要ありません。練習したい場合は、少し再帰的なパーサーを書くことができますが、それでもかっこを一致させるためには少し大変です。

21
Jesse Beder

これは、ジョブに間違ったツールを使用する良い例です。正規表現は、任意にネストされたサブマッチをうまく処理しません。代わりにすべきことは、実際のレクサーとパーサーを使用し(C++の文法は簡単に見つけられるはずです)、予期せず空のループ本体を探すことです。

8
Greg Hewgill

括弧の内容にも注意を払いません。

forで始まりセミコロンで終わる行に一致するだけです。

^\t*for.+;$

forステートメントが複数行に分割されていない限り、それはうまくいきますか?

2
Peter Boughton

この正規表現を試してください

_^\s*(for|while)\s*
\(
(?P<balanced>
[^()]*
|
(?P=balanced)
\)
\s*;\s
_

_(?P=balanced)_の周りのラッピング\( \)を削除し、_*_を任意の非括弧シーケンスの後ろに移動しました。 boost xpressiveを使用してこの作業を行い、そのWebサイト( Xpressive )を再確認して記憶を更新しました。

2
Bill Perkins

グレッグは絶対に正しい。この種の解析は、正規表現では実行できません。私は、多くの場合に機能する恐ろしい怪物を構築することは可能だと思いますが、そうすることで何かに出くわすでしょう。

より伝統的な解析手法を使用する必要があります。たとえば、必要なことを行うために再帰的な適切なパーサーを作成するのは非常に簡単です。

1
Foredecker

正規表現がそのようなものをうまく処理できるかどうかはわかりません。このようなものを試してください

line = line.Trim();
if(line.StartsWith("for") && line.EndsWith(";")){
    //your code here
}
1
Malfist

括弧を無視し、forをセミコロンで区切られた3つの値を保持する構造として扱う別の考え方:

_for\s*\([^;]+;[^;]+;[^;]+\)\s*;
_

このオプションは、複数行に分割した場合でも機能します(MULTILINEが有効になっている場合)が、for ( ... ; ... ; ... )が唯一の有効な構成体であると想定しているため、for ( x in y )構成体またはその他の偏差では機能しません。

また、次のようなセミコロンを引数として含む関数がないと仮定します。

_for ( var i = 0; i < ListLen('a;b;c',';') ; i++ );
_

これが起こりそうなケースであるかどうかは、あなたが実際にこれをしていることに依存します。

1
Peter Boughton

パーティーに少し遅れましたが、正規表現は仕事に適したツールではないと思います

問題は、正規表現に余分な複雑さを追加するEdgeのケースに出くわすことです。 @ est 言及 行の例

for (int i = 0; i < 10; doSomethingTo("("));

この文字列リテラルには、(不均衡な!)括弧が含まれており、これによりロジックが中断されます。どうやら、文字列リテラルの内容を無視する必要があります。これを行うには、二重引用符を考慮する必要があります。ただし、文字列リテラル自体には二重引用符を含めることができます。たとえば、これを試してください:

for (int i = 0; i < 10; doSomethingTo("\"(\\"));

正規表現を使用してこれに対処すると、パターンがさらに複雑になります。

言語を解析する方が良いと思います。たとえば、ANTLRなどの言語認識ツールを使用できます。 ANTLRは、 Pythonのパーサー も生成できるパーサージェネレーターツールです。ターゲット言語を定義する文法を提供する必要があります(C++の場合)。すでに多くの言語用の多数の文法が存在するため、 C++文法 を取得するだけで済みます。

次に、whileまたはforループ本体として空のステートメントを検索して、パーサーツリーを簡単にたどることができます。

0
MC Emperor

フランクが示唆したように、これは正規表現なしで最高です。ここに(い)ワンライナーがあります:

match_string = orig_string[orig_string.index("("):len(orig_string)-orig_string[::-1].index(")")]

彼のコメントで言及されたトロールラインと一致する:

orig_string = "for (int i = 0; i < 10; doSomethingTo(\"(\"));"
match_string = orig_string[orig_string.index("("):len(orig_string)-orig_string[::-1].index(")")]

戻り値 (int i = 0; i < 10; doSomethingTo("("))

これは、最初の開いた括弧に達するまで文字列を順方向に実行し、最初の閉じ括弧に達するまで逆方向に実行することで機能します。次に、これら2つのインデックスを使用して文字列をスライスします。

0
bendl