1

テキスト内の有効な Java 注釈を検出しようとしています。これが私のテストプログラムです(現在、簡単にするためにすべての空白を無視しています。後で追加します):

txts = ['@SomeName2',                   # match
        '@SomeName2(',                  # no match
        '@SomeName2)',                  # no match 
        '@SomeName2()',                 # match
        '@SomeName2()()',               # no match
        '@SomeName2(value)',            # no match
        '@SomeName2(=)',                # no match
        '@SomeName2("")',               # match
        '@SomeName2(".")',              # no match
        '@SomeName2(",")',              # match
        '@SomeName2(value=)',           # no match
        '@SomeName2(value=")',          # no match
        '@SomeName2(=3)',               # no match
        '@SomeName2(="")',              # no match
        '@SomeName2(value=3)',          # match
        '@SomeName2(value=3L)',         # match
        '@SomeName2(value="")',         # match
        '@SomeName2(value=true)',       # match
        '@SomeName2(value=false)',      # match
        '@SomeName2(value=".")',        # no match
        '@SomeName2(value=",")',        # match
        '@SomeName2(x="o_nbr ASC, a")', # match

        # multiple params:
        '@SomeName2(,value="ord_nbr ASC, name")',                            # no match
        '@SomeName2(value="ord_nbr ASC, name",)',                            # no match
        '@SomeName2(value="ord_nbr ASC, name"insertable=false)',             # no match
        '@SomeName2(value="ord_nbr ASC, name",insertable=false)',            # match
        '@SomeName2(value="ord_nbr ASC, name",insertable=false,length=10L)', # match

        '@SomeName2 ( "ord_nbr ASC, name", insertable = false, length = 10L )',       # match
       ]


#regex = '((?:@[a-z][a-z0-9_]*))(\((((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))?\))?$'
#regex = '((?:@[a-z][a-z0-9_]*))(\((((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))?(,((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))*\))?$'

regex = r"""
    (?:@[a-z]\w*)                               # @ + identifier (class name)
    (
      \(                                        # opening parenthesis
        (
          (?:[a-z]\w*)                          # identifier (var name)
          =                                     # assigment operator
          (\d+l?|"(?:[a-z0-9_, ]*)"|true|false) # either a numeric | a quoted string containing only alphanumeric chars, _, space | true | false
        )?                                      # optional assignment group
      \)                                        # closing parenthesis
    )?$                                         # optional parentheses group (zero or one)
    """


rg = re.compile(regex, re.VERBOSE + re.IGNORECASE)

for txt in txts:
    m = rg.search(txt)
    #m = rg.match(txt)
    if m:
        print "MATCH:   ",
        output = ''
        for i in xrange(2):
            output = output + '[' + str(m.group(i+1)) + ']'
        print output
    else:
        print "NO MATCH: " + txt

したがって、基本的に私が持っているものは、ゼロまたは1つのパラメーターで機能するようです。ここで、最後の例のように、構文を 0 個以上のパラメーターに拡張しようとしています。

次に、割り当てを表す正規表現部分をコピーし、2 番目から n 番目のグループ (このグループでは ? の代わりに * を使用しています) のコンマを先頭に追加します。

regex = '((?:@[a-z][a-z0-9_]*))(\((((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))?(,((?:[a-z][a-z0-9_]*))(=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))*\))?$'

ただし、それは機能しません。問題は、最初の要素を処理する方法にあるようです。これはオプションである必要があるため、最初の拡張例のような文字列'@SomeName2(,value="ord_nbr ASC, name")'が受け入れられますが、これは間違っています。2番目からn番目の割り当てを最初の(オプションの)要素の存在のみに依存させる方法がわかりません。

それはできますか?そのようにされていますか?これをどのように解決するのが最善ですか?

ありがとう

4

2 に答える 2

2

有効な構文を検出しようとしているだけなら、以下の正規表現で必要な一致が得られると思います。しかし、あなたがグループで何をしているのかわかりません。各パラメーター値も独自のグループに入れますか? それはもっと難しいでしょうし、正規表現でそれが可能かどうかさえわかりません.

regex = r'((?:@[a-z][a-z0-9_]*))(?:\((?!,)(?:(([a-z][a-z0-9_]*(=)(?:("[a-z0-9_, ]*")|(true|false)|(\d+l?))))(?!,\)),?)*\)(?!\()|$)'

個々のパラメーター/値が必要な場合は、おそらくそのための実際のパーサーを作成する必要があります。

編集: これはコメント付きのバージョンです。また、理解しやすくするために、キャプチャ グループと非キャプチャ グループの多くを削除しました。これを使用するとre.findall()、関数名と括弧内のすべてのパラメーターの 2 つのグループが返されます。

regex = r'''
(@[a-z][a-z0-9_]*) # function name, captured in group
(                  # open capture group for all parameters
\(                 # opening function parenthesis 
  (?!,)            # negative lookahead for unwanted comma
  (?:              # open non-capturing group for all params
  [a-z][a-z0-9_]*  # parameter name
  =                # parameter assignmentoperators
  (?:"[a-z0-9_, ]*"|true|false|(?:\d+l?)) # possible parameter values
  (?!,\))          # negative lookahead for unwanted comma and closing parenthesis
  ,?               # optional comma, separating params
  )*               # close param non-capturing group, make it optional
\)                 # closing function parenthesis 
(?!\(\))           # negative lookahead for empty parentheses
|$                 # OR end-of-line (in case there are no params)
)                  # close capture group for all parameters
'''

パラメーターに関するコメントを読んだ後、おそらく最も簡単な方法は、上記の正規表現を使用してすべてのパラメーターを引き出し、次に別の正規表現を記述して、必要に応じて処理する名前/値のペアを引き出すことです。ただし、パラメーター値にコンマがあるため、これも注意が必要です。私はそれを読者のための演習として残しておきます:)

于 2012-04-09T10:57:54.963 に答える
1

re.VERBOSE フラグを使用する

あなたはここで面白いことをしました。元の正規表現は次のとおりです。

regex = '((?:@[a-z][a-z0-9_]*))(\((((?:[a-z][a-z0-9_]*))(=)(\d+l?|"
(?:[a-z0-9_, ]*)"|true|false))?\))?$'

手始めに、re.VERBOSE フラグを使用して、これを複数行に分割できるようにします。このように、正規表現の空白やコメントはその意味に影響を与えないため、正規表現が何をしようとしているのかを文書化できます。

regex = re.compile("""
((?:@[a-z][a-z0-9_]*))     # Match starting symbol, @-sign followed by a word
(\(
    (((?:[a-z][a-z0-9_]*))                     # Match arguments??
    (=)(\d+l?|"(?:[a-z0-9_, ]*)"|true|false))? # ?????
\))?$
""", re.VERBOSE + re.IGNORECASE)

この正規表現が何をしようとしているのかを文書化していないので、これ以上分解することはできません。re.VERBOSE を使用し、複数行に分割してコメントすることにより、重要な正規表現の意図を文書化します。


問題を扱いやすい部分に分割する

あなたの正規表現は、やりすぎているため、理解するのが非常に困難です。現状では、正規表現は次の 2 つのことを行おうとしています。

  1. の形式のシンボル名に一致し@SomeSymbol2、オプションでその後に括弧で囲まれた引数のリストが続きます。(arg1="val1",arg2="val2"...)
  2. かっこで囲まれた引数リストの内容を検証して、(arg1="val1",arg2="val2")合格するが合格(232,211)しないようにします。

以下のように、これを 2 つの部分に分割することをお勧めします。

import re
import pprint

txts = [
        '@SomeName2',              # match
        '@SomeName2(',             # no match
        '@SomeName2)',             # no match 
        '@SomeName2()',            # match
        '@SomeName2()()',          # no match
        '@SomeName2(value)',       # no match
        '@SomeName2(=)',           # no match
        '@SomeName2("")',          # no match
        '@SomeName2(value=)',      # no match
        '@SomeName2(value=")',     # no match
        '@SomeName2(=3)',          # no match
        '@SomeName2(="")',         # no match
        '@SomeName2(value=3)',     # match
        '@SomeName2(value=3L)',    # match
        '@SomeName2(value="")',    # match
        '@SomeName2(value=true)',  # match
        '@SomeName2(value=false)', # match
        '@SomeName2(value=".")',   # no match
        '@SomeName2(value=",")',   # match
        '@SomeName2(value="ord_nbr ASC, name")', # match

        # extension needed!:
        '@SomeName2(,value="ord_nbr ASC, name")', # no match
        '@SomeName2(value="ord_nbr ASC, name",)', # no match
        '@SomeName2(value="ord_nbr ASC, name",insertable=false)'
        ] # no match YET, but should

# Regular expression to match overall @symbolname(parenthesised stuff)
regex_1 = re.compile( r"""
^                   # Start of string
(@[a-zA-Z]\w*)      # Matches initial token. Token name must start with a letter.
                    # Subsequent characters can be any of those matched by \w, being [a-zA-Z0-9_]
                    # Note behaviour of \w is LOCALE dependent.
( \( [^)]* \) )?    # Optionally, match parenthesised part containing zero or more characters
$                   # End of string
""", re.VERBOSE)

#Regular expression to validate contents of parentheses
regex_2 = re.compile( r"""
^
(
    ([a-zA-Z]\w*)       # argument key name (i.e. 'value' in the examples above)
    =                   # literal equals symbol
    (                   # acceptable arguments are:
        true  |         # literal "true"
        false |         # literal "false"
        \d+L? |         # integer (optionally followed by an 'L')
        "[^"]*"         # string (may not contain quote marks!)
    )
    \s*,?\s*            # optional comma and whitespace
)*                      # Match this entire regex zero or more times
$
""", re.VERBOSE)

for line in txts:
    print("\n")
    print(line)
    m1 = regex_1.search(line)    

    if m1:
        annotation_name, annotation_args = m1.groups()

        print "Symbol name   : ", annotation_name
        print "Argument list : ", annotation_args

        if annotation_args:
            s2 = annotation_args.strip("()")
            m2 = regex_2.search(s2)
            if (m2):
                pprint.pprint(m2.groups())
                print "MATCH"
            else:
                print "MATCH FAILED: regex_2 didn't match. Contents of parentheses were invalid."
        else:
            print "MATCH"

    else:
        print "MATCH FAILED: regex_1 didn't match."

これにより、ほぼ最終的な解決策が得られます。私が見ることができる唯一のコーナーケースは、これが(誤って)引数リストの末尾のコンマと一致することです。(これは、単純な文字列操作を使用して確認できますstr.endswith()。)


後付け編集: 引数リストの構文は、実際には実際のデータ形式にかなり近いです。おそらくargument_list、JSON または YAML パーサーにフィードすると、それが適切かどうかがわかります。可能であれば、車輪を再発明するのではなく、既存の車輪 (JSON パーサー) を使用します。

これにより、とりわけ-

  • 浮動小数点数などを含む、Javascript がサポートするすべての引数タイプの認識
  • 文字列内のエスケープされた引用符のサポート。"This is a quote mark: \"."現在、正規表現は2 番目の引用符で文字列が終了していると見なされるため、barf して終了します。(そうではありません。)

これは正規表現で行うことができますが、恐ろしく複雑です。

于 2012-04-09T11:28:03.470 に答える