1

PHPで正しい正規表現を記述して、(一部のユーザーが記述した)文字列を解析してリクエストを作成しようとしています。次のように複雑にすることができます。

name = 'benjo' and (surname = 'benny' or surname = 'bennie') or age = 4

後で、文字列を解析して mySQL クエリを作成します。今のところ、この文字列を次のような配列に解析するための正しい正規表現を見つけようとしています。

$result = array(
0 => name = 'benjo',
1 => and
2 => array(
    0 => surname = 'benny',
    1 => or,
    2 => surname = 'bennie',
    ),
3 => age = 4
);

再帰関数を使用することを考えましたが、今のところ正規表現は次のとおりです。

"#\((([^()]+|(?R))*)\)|(ou[^()])|(et[^()])#",

もちろん機能しません。

誰かが助けてくれたら嬉しいです、私はここで立ち往生しています! :) Tks、ロマン

チャレンジを変えよう!:) OK、ではもう少しシンプルにしましょう。正規表現を使用して、「レベル 1」にとどまるという制約を追加するとどうなるでしょうか !! ネストされた括弧はなく、1 つのレベルだけですが、それでも多くの AND/OR があります。(ミニパーサーを書くことは本当に避けたいと思っていますが、それは本当に興味深いように思えます...

4

1 に答える 1

4

理論上の正規表現は、括弧のマッチングを行うほど強力ではありません。理論上の正規表現は、左再帰/右再帰ルールのみを処理できます。中再帰規則は、正規表現 ( など<exp> -> "(" <exp> ")") では表現できません。

ただし、プログラミング言語の正規表現は、正規表現が通常の文法の力を超えることを可能にする機能を実装しています。たとえば、正規表現の後方参照を使用すると、非文脈自由言語に一致する正規表現を記述できます。ただし、後方参照を使用しても、括弧と正規表現のバランスを取ることはまだできません。

PCRE ライブラリはサブルーチン呼び出し機能を介して再帰的な正規表現をサポートしているため、このような式を正規表現で解析することは技術的に可能です。ただし、正規表現を自分で作成できる場合 (つまり、自分が何をしているのかを理解し、必要に応じて正規表現を変更できる場合) を除き、独自のパーサーを作成する必要があります。そうしないと、維持できない混乱が発生します。

(?(DEFINE)
  (?<string>'[^']++')
  (?<int>\b\d+\b)
  (?<sp>\s*)
  (?<key>\b\w+\b)
  (?<value>(?&string)|(?&int))
  (?<exp>(?&key) (?&sp) = (?&sp) (?&value))
  (?<logic>\b (?:and|or) \b)
  (?<main>
    (?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) )
    (?:
      (?&sp) (?&logic) (?&sp)
      (?&token) 
    )*
  )
)
(?:
  ^ (?&sp) (?= (?&main) (?&sp) $ )
  |
  (?!^) \G
  (?&sp) (?&logic) (?&sp)
)
(?:
  \( (?&sp) (?<m_main>(?&main)) (?&sp) \)
  |
  (?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value))
)

regex101 のデモ

上記の正規表現は とともに使用しpreg_match_all、 x フラグ (フリー スペーシング モード) で区切り文字の間に配置する必要があります/.../x

試合ごとに:

  • キャプチャ グループにコンテンツがある場合m_mainは、そのコンテンツを別の照合ラウンドにかけます。
  • m_keyそれ以外の場合は、m_valueキャプチャ グループでキーと値を取得します。

説明

この(?(DEFINE)...)ブロックを使用すると、メイン パターンとは別に、サブルーチン呼び出しで使用する名前付きキャプチャ グループを定義できます。

(?(DEFINE)
  (?<string>'[^']++')  # String literal
  (?<int>\b\d+\b)      # Integer
  (?<sp>\s*)           # Whitespaces between tokens
  (?<key>\b\w+\b)      # Field name
  (?<value>(?&string)|(?&int)) # Field value
  (?<exp>(?&key) (?&sp) = (?&sp) (?&value)) # Simple expression
  (?<logic>\b (?:and|or) \b) # Logical operators
  (?<main>             # <token> ( <logic> <token> )*
    # A token can contain a simple expression, or open a parentheses (...)
    # When we open a parentheses, we recurse into the main pattern again
    (?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) )
    (?:
      (?&sp) (?&logic) (?&sp)
      (?&token) 
    )*
  )
)

パターンの残りの部分は、この手法に基づいて、すべて<token><token> ( <logic> <token> )*をグローバル マッチング操作で照合します。

正規表現の最後の部分は、 のように記述できますが(?&token)、単純な式のフィールド名と値に一致するように展開されます。

(?:
  \( (?&sp) (?<m_main>(?&main)) (?&sp) \)
  |
  (?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value))
)
于 2012-05-27T08:28:20.883 に答える