一般来说,Or、And、MatchFirst 和 Each 类很少在 pyparsing 中公开使用。推荐的风格是使用它们类似的运算符重载。在您的情况下,您正在使用这两种形式,它只是妨碍您。
这是你的表达,经过一点清理:
key = Word(alphanums + "/-_.?=%&")
QUOT = Suppress('"')
uri = ("-" | QUOT
+ http_method
+ key("request_uri")
+ http_protocol
+ QUOT
)
Word 的参数是表示允许的字符集的字符串。如果只使用一个参数(如您的情况),则字符串被解释为简单的字符集,可以作为 Word 的一部分进行解析。如果给出 2 个字符串,则第一个表示可接受的初始字符集,第二个是可接受的主体字符集(在定义变量名之类的东西时很有用,例如在 Python 中只允许使用字母和 '_'初始字符,但也允许在正文中出现数字。这将是Word(alphas+'_', alphanums+'_')
。由于 Word 的参数只是字符串,因此无需单独添加"/" + "-" + "_" + ...
,只需将它们组合成一个字符串即可。
'|' 运算符分隔允许的备选方案,生成 MatchFirst 表达式。之所以称为 MatchFirst,是因为解析器将在第一个给定表达式匹配后停止尝试。因此,如果用 解析字符串“abc” Word(alphas) | Word(nums)
,pyparsing 甚至不会尝试匹配Word(nums)
表达式 - 第一个替代匹配。如果您想要的内容有一些重叠,这会变得更加棘手。假设您要匹配字母词、字母词或字母和字母词,并且要解析字符串“abc123”。这个解析器:
Word(alphas) | Word(nums) | Word(alphanums)
将解析带有前导的字符串的开头 'abc' Word(alphas)
。我们通常可以通过重新安排替代方案来解决此类问题,例如:
Word(alphanums) | Word(alphas) | Word(nums)
但并非所有情况都如此容易重构。所以 pyparsing 还支持 Or 表达式,使用 '^' 运算符定义(我选择它是因为 '^' 让我想起一对绘图员的分隔线,用于测量长度)。Or 表达式尝试应用所有给定的备选方案,并选择最长的匹配项。因此,您可以将我的小测试示例编写为:
Word(alphas) ^ Word(nums) ^ Word(alphanums)
现在 pyparsing 在匹配“abc”时不会停止,而是会尝试所有替代方案,并最终选择第三个替代方案,匹配“abc123”,因为它提供了更长的匹配。
对于你的 URI 定义,不需要做 Or 匹配。解析器不会将前导“-”与带引号的 HTTP 命令字符串混淆。所以使用 MatchFirst,你已经通过使用 '|' 运营商,完全够用。
其他一些项目:
"\""
如果可以,请不要使用 Python 编写代码。出于这个原因,Python 支持这两种引用字符。改为使用'"'
。反斜杠用于 C 程序员和 Windows 文件名。
expr.setResultsName("name")
expr("name")
自 pyparsing 1.4.6 以来已简化为。缩短的语法确实有助于您的解析器定义的可读性。
仅当您希望在结果中保留某些结构,或者如果您有一个重复结构,其中包含带有结果名称的某些内部表达式时,才使用 Group。您的解析器并不真正需要,只需在结果上添加另一个列表容器包装器,需要一个额外的[0]
索引来获取您解析的数据。
(如果您确实决定要显式调用Or
,And
等,请确保传递表达式列表,并且不要仅将它们列为表达式构造函数的参数 - 请参阅Why is ordered choice in pyparsing failed for my use案例?关于这样的拼写错误会如何搞砸事情,这就是为什么我鼓励使用算术运算符来组成你的解析器。)