4

私はpyparsingを使用しており、次のように2つのアイテムを定義しようとしています:

identifier = Word(alphas, alphanums).setName('identifier)

database_name = Optional(identifier.setResultsName('user') + Suppress('.')) + identifier.setResultsName('database')
table_name = database_name + Suppress('.') + identifier.setResultsName('table')

と照合するとtable_name、2 つまたは 3 つのセグメントを含む文字列が取得され、次のようになります。

mark.foo.bar
=> tokens.user = 'mark'
   tokens.database = 'foo'
   tokens.table = 'bar'

または、最初のセグメントがない場合:

foo.bar
=> tokens.user = ''  #anything is acceptable: none, empty string or just plain missing
   tokens.database = 'foo'
   tokens.table = 'bar'

table_nameは、常に 2 つのセグメントと 1 つのドット、または上記のように 3 つのセグメント (2 つのドット) を持つ必要があります。ワンセグは不可です。

database_name1 つのセグメント (データベース) または 2 つのセグメント (user.database) が必要です。

使用のインスタンスはdatabase_name問題なく動作します - 1 つまたは 2 つのセグメントで一致します。ただし、table_name場合によっては失敗します。

# Works for three segments
mark.foo.bar
=> tokens.user = 'mark'
   tokens.database = 'foo'
   tokens.table = 'bar'

# Fails for two
foo.bar
=> Expected "." (at char 7), (line:1m col:8)

私はそれが何をしているのかを見ることができます: foo.baruser.database に一致し、テーブル名を表す 3 番目のチャンクを期待しています。しかし、それは私が望むものではありません。

ヘルプ?

4

1 に答える 1

5

問題は、先頭の識別子を突き合わせても、それがユーザー フィールドになるかどうかを判断するには、考えられるすべてのテーブル フィールドを確認するまでわからないことです。残念ながら、これは、先頭のオプションの「ユーザー」フィールドだけでデータベース名を定義できないことを意味しますtable_name。2 つまたは 3 つのフィールドを持つ包括的な式を定義する必要があります。

次のコードは、先頭のオプション識別子のあいまいさを解決するための 3 つのオプションを示しています。

  1. 最初に完全な 3 フィールド フォームの一致を試み、それが失敗した場合は 2 フィールド フォームの一致を試みます。

  2. オプションの先頭の「user」フィールドに一致する場合は明示的に先読みし、FollowedBy を使用して、後に続く場合にのみ「user」に一致させます。2*(DOT+identifier)

  3. 任意の長さのすべてのドット区切りリストに一致し、解析アクションを使用して 2 つまたは 3 つの識別子のみが渡されたことを確認し、結果の名前を割り当てます。

各オプションの実装方法については、コメントを参照してください。(コードを単純化するために、 full の使用expr.setResultsName('something')を justexpr('something')に置き換えたことにも注意してください。全体的に読みやすいと思います。)

from pyparsing import *

identifier = Word(alphas, alphanums).setName('identifier')
DOT = Suppress('.')

# Option 1 - fully specified options
full_database_name = identifier('user') + DOT + identifier('database')
just_database_name = identifier('database')
table_name = (full_database_name + DOT + identifier('table') | 
              just_database_name + DOT + identifier('table'))

# Option 2 - use FollowedBy to explicitly lookahead when checking for leading user
table_name = (Optional(identifier('user') + FollowedBy(2*(DOT+identifier)) + DOT) + 
                identifier('database') + DOT + identifier('table'))

# Option 3 - use liberally matching expression, with a parse action to assign fields
def assignTableFields(fields):
    if len(fields) == 2:
        fields['database'],fields['table'] = fields
    elif len(fields) == 3:
        fields['user'],fields['database'],fields['table'] = fields
    else:
        raise ParseException("wrong number of fields")
table_name = delimitedList(identifier, delim='.').setParseAction(assignTableFields)

for test in ("a.b.c", "b.c"):
    print test
    print table_name.parseString(test).dump()
    print

また、インターリーブされた空白も許可されているため"a . b"、有効なテーブル名としての資格があるため、マッチャーが過度にリベラルであることに気付くかもしれません。別の検証解析アクションを定義して、次のように table_name に追加できます。

def noWhitespace(source, locn, tokens):
    if not source[locn:].startswith('.'.join(tokens)):
        raise ParseException("found whitespace between fields")
table_name.addParseAction(noWhitespace)

この解析アクションでは、addParseAction代わりにを呼び出しsetParseActionて、既存の解析アクションが保持されるようにし (オプション 3 の場合)、この新しい解析アクションが実行される解析アクションのチェーンに追加されていることを確認してください。

于 2012-06-24T22:47:58.793 に答える