python - pyparsing 中的可选字符串段

标签 python pyparsing

我正在使用 pyparsing 并尝试定义如下两个项目:

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,它会接受一个包含两个或三个片段的字符串,结果如下:

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 应该总是有两个段和一个点,或者如上所述的三个段(两个点)。一个片段是 Not Acceptable 。

database_name 应该有一个段(数据库)或两个段(user.database)。

使用 database_name 的实例工作正常 - 它会匹配一个或两个段。但是,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.bar 已经与 user.database 匹配,它现在期待第三个 block 表示表名。然而这不是我想要的。

帮忙吗?

最佳答案

问题是,当您匹配前导标识符时,您不知道它是否将成为用户字段,直到您查看了所有可能的表字段。不幸的是,这意味着您不能单独定义具有前导可选“用户”字段的数据库名称,您必须定义一个综合的 table_name 表达式,具有两个或三个字段。

下面的代码显示了 3 个选项来解决前导可选标识符的歧义:

  1. 首先尝试匹配完整的 3 字段形式,如果失败,则尝试匹配 2 字段形式

  2. 在匹配可选的前导“用户”字段时显式向前看,仅当“用户”后跟 2*(DOT+identifier)

    时才使用 FollowedBy 匹配“用户”
  3. 匹配任何长度的所有点分隔列表,并使用解析操作来验证是否只传递了 2 或 3 个标识符,并分配结果名称

查看评论以了解每个选项是如何实现的。 (请注意,为了简化代码,我还将完整的 expr.setResultsName('something') 替换为 expr('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 的情况下),并且这个新的添加到要运行的解析操作链中。

关于python - pyparsing 中的可选字符串段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11180622/

相关文章:

Python '&' 字符丢失

python - Python 的通用桥接框架

python - 当想要查找得分最高的 `start` 标记时,torch.argmax() 中出现 TypeError

python - 区分pyparsing中的匹配项

python - Pyparsing - 在行开头匹配文字,忽略空格

python - 对段落进行 Pyparsing

javascript - 用方括号包裹单词而不是 sglQuotedString 或 dblQuotedString

python - Pandas 数据框返回 None 值

python - 有没有办法从图形图像中提取数据并将其存储在 Excel 工作表或 csv 文件中?

python - 我如何告诉 pyparsing 丢弃部分已解析的字符串?