2

したがって、次のように文字列を受け入れたいとしましょう
SomeColumn IN||<||>||= [123, 'hello', "wassup"]||123||'hello'||"yay!"
。例:MyValue IN ['value', 123]またはMyInt > 123->あなたはその考えを理解していると思います。さて、私を悩ませているのは、正規表現でこれをどのように表現するかです。私はPHPを使用していますが、これが現在行っていることです。

        $temp = explode(';', $constraints);
        $matches = array();
        foreach ($temp as $condition) {
            preg_match('/(.+)[\t| ]+(IN|<|=|>|!)[\t| ]+([0-9]+|[.+]|.+)/', $condition, $matches[]);
        }
        foreach ($matches as $match) {
            if ($match[2] == 'IN') {
                preg_match('/(?:([0-9]+|".+"|\'.+\'))/', substr($match[3], 1, -1), $tempm);
                print_r($tempm);
            }
        }
そこにある助けに本当に感謝しています。私の正規表現はひどいものです。

4

1 に答える 1

0

入力は次のようになります。

$string = 'SomeColumn IN [123, \'hello\', "wassup"];SomeColumn < 123;SomeColumn = \'hello\';SomeColumn > 123;SomeColumn = "yay!";SomeColumn = [123, \'hello\', "wassup"]';

使用する場合、マッチを自分で作成するpreg_match_all必要はありません。explode結果として得られる 2 次元配列の次元が入れ替わることに注意してください。ただし、多くの場合、これは望ましいことです。コードは次のとおりです。

preg_match_all('/(\w+)[\t ]+(IN|<|>|=|!)[\t ]+((\'[^\']*\'|"[^"]*"|\d+)|\[[\t ]*(?4)(?:[\t ]*,[\t ]*(?4))*[\t ]*\])/', $string, $matches);

$statements = $matches[0];
$columns = $matches[1];
$operators = $matches[2];
$values = $matches[3];

もありますが、$matches[4]実際には意味がなく、正規表現内でのみ使用されます。まず、あなたの試みで間違ったことをいくつか:

  • (.+)できるだけ多く、そしてどのキャラクターも消費します。したがって、次のような文字列値の中に何かがある場合IN 13、最初の繰り返しはそこまですべてを消費し、それを列として返す可能性があります。また、空白と=列名の内側も許可されます。これには 2 つの方法があります。追加して繰り返しを「貪欲に」する?か、さらに良いことに、許可される文字を制限して、目的の区切り文字を超えることができないようにします。私の正規表現では\w、列識別子に文字、数字、およびアンダースコア ( ) のみを許可します。
  • [\t| ]これは、代替と文字クラスという 2 つの概念を混同しています。これが行うことは、「タブ、パイプ、またはスペースに一致する」ことです。文字クラスでは、すべての文字を区切らずに単純に記述します。(\t| )または、この場合はどちらが同等であるかを書くこともできます。
  • [.+]これで何を達成しようとしていたのかわかりませんが、リテラル.またはリテラルのいずれかに一致します+。また、許可される文字を制限し、引用符が正しく一致しているかどうかを確認すると便利な場合があります (回避するため'some string") 。

ここで、私自身の正規表現について説明します (これをコードにコピーすることもできますが、問題なく動作します。さらに、コード内にコメントとして説明があります)。

preg_match_all('/
    (\w+)           # match an identifier and capture in $1
    [\t ]+          # one or more tabs or spaces
    (IN|<|>|=|!)    # the operator (capture in $2)
    [\t ]+          # one or more tabs or spaces
    (               # start of capturing group $3 (the value)
        (           # start of subpattern for single-valued literals (capturing group $4)
            \'      # literal quote
            [^\']*  # arbitrarily many non-quote characters, to avoid going past the end of the string
            \'      # literal quote
        |           # OR
            "[^"]*" # equivalent for double-quotes
        |           # OR
            \d+     # a number
        )           # end of subpattern for single-valued literals
    |               # OR (arrays follow)
        \[          # literal [
        [\t ]*      # zero or more tabs or spaces
        (?4)        # reuse subpattern no. 4 (any single-valued literal)
        (?:         # start non-capturing subpattern for further array elements
            [\t ]*  # zero or more tabs or spaces
            ,       # a literal comma
            [\t ]*  # zero or more tabs or spaces
            (?4)    # reuse subpattern no. 4 (any single-valued literal)
        )*          # end of additional array element; repeat zero or more times
        [\t ]*      # zero or more tabs or spaces
        \]          # literal ]
    )               # end of capturing group $3
    /',
    $string,
    $matches);

これは、サブパターン (または正規表現全体) を再利用できる PCRE の再帰機能を利用します(?n)(ここでn、 は後方参照にも使用する数値です)。

この正規表現で改善できる主な点は 3 つあります。

  • 浮動小数点数は許可されていません
  • エスケープされた引用符は許可されません (値が の場合'don\'t do this'、私は captur のみを使用します'don\')。これは、否定後読みを使用して解決できます。
  • 空の配列を値として使用することはできません (これは、すべてのパラメーターをサブパターンでラップし、 でオプションにすることで簡単に解決できます?) 。

それらがあなたの問題に当てはまるかどうかわからなかったので、これらのどれも含めませんでした.正規表現はすでにここに提示するのに十分複雑であると思いました.

通常、正規表現は適切な言語解析を行うほど強力ではありません。一般的には、パーサーを作成する方が適切です。

そして、あなたの正規表現は恐ろしいと言ったので... 正規表現はその珍しい構文のために多くの黒魔術のように見えますが、基本的なことを理解するために一度時間を割けば、理解するのはそれほど難しくありません.概念。このチュートリアルをお勧めします。それは本当にあなたをずっと連れて行ってくれます!

于 2012-11-13T22:11:15.897 に答える