
在数据处理中,我们经常需要解析各种格式的字符串。其中一种常见的挑战是解析逗号分隔的字符串数组,这些数组可能包含空元素,并且被括号包裹。例如,(,,"my","cool",,"array",,,)就是一个典型的例子,其中多个逗号表示空元素,我们希望将它们表示为none。
使用解析器生成器(如Parsimonious,一个基于解析表达式文法PEG的Python库)来处理这类结构时,一个常见的陷阱是构建的语法规则可能过于宽松,从而接受不符合预期的非法格式。例如,一个简单的规则可能错误地将("My""Cool""Array")这样的缺失逗号分隔符的字符串视为有效输入。理想情况下,我们希望在解析阶段就能检测到这类错误,而不是在后续遍历抽象语法树(AST)时才发现。
为了应对上述挑战,我们需要设计一个能够精确匹配目标格式并拒绝非法输入的Parsimonious语法。核心思想是明确指定每个元素可以是字符串或空值,并且它们之间必须由逗号分隔。
首先,我们定义构成数组的最小单元:字符串和逗号。
字符串 (string):一个被双引号包围的非引号字符序列。
string = ~'"[^\"]+"'
这里,~表示这是一个正则表达式规则。"[^\"]+"匹配一个双引号,后面跟着一个或多个非双引号字符,最后以一个双引号结束。
逗号 (comma):简单的逗号字符。
comma = ","
这是最关键的部分,它决定了数组的整体结构以及如何处理空元素。
array = "(" string? (comma string?)* ")"让我们逐一解析这条规则:
将这些规则组合起来,就得到了完整的Parsimonious语法:
from parsimonious import Grammar
grammar = Grammar('''
array = "(" string? (comma string?)* ")"
string = ~'"[^\"]+"'
comma = ","
''')现在,我们可以使用这个语法来测试不同类型的输入,验证其鲁棒性。
from parsimonious import Grammar, ParseError
# 定义Parsimonious语法
grammar = Grammar('''
array = "(" string? (comma string?)* ")"
string = ~'"[^\"]+"'
comma = ","
''')
# 测试有效输入
valid_inputs = [
'("My","Cool","Array")', # 正常数组
'("My","Cool","Array",)', # 带末尾逗号的数组
'(,,"My","Cool",,"Array",,,)', # 包含多个空元素的复杂数组
'()', # 空数组
'(,"OnlyString")', # 首元素为空
'("OnlyString",)', # 尾元素为空
'("OnlyString")', # 单元素数组
]
print("--- 有效输入测试 ---")
for i, input_str in enumerate(valid_inputs):
try:
grammar.parse(input_str)
print(f"[{i+1}] '{input_str}' -> 解析成功")
except ParseError as e:
print(f"[{i+1}] '{input_str}' -> 解析失败 (意外): {e}")
print("\n--- 无效输入测试 ---")
# 测试无效输入
invalid_inputs = [
'("My""Cool""Array")', # 缺少逗号分隔符
'(My,Cool,Array)', # 字符串未加引号
'("My","Cool",Array)', # 混合格式
'["My","Cool"]', # 错误的外层括号
'("My","Cool",', # 未闭合的括号
]
for i, input_str in enumerate(invalid_inputs):
try:
grammar.parse(input_str)
print(f"[{i+1}] '{input_str}' -> 解析成功 (意外)")
except ParseError:
print(f"[{i+1}] '{input_str}' -> 解析失败 (符合预期)")
输出示例:
--- 有效输入测试 ---
[1] '("My","Cool","Array")' -> 解析成功
[2] '("My","Cool","Array",)' -> 解析成功
[3] '(,,"My","Cool",,"Array",,,)' -> 解析成功
[4] '()' -> 解析成功
[5] '(,"OnlyString")' -> 解析成功
[6] '("OnlyString",)' -> 解析成功
[7] '("OnlyString")' -> 解析成功
--- 无效输入测试 ---
[1] '("My""Cool""Array")' -> 解析失败 (符合预期)
[2] '(My,Cool,Array)' -> 解析失败 (符合预期)
[3] '("My","Cool",Array)' -> 解析失败 (符合预期)
[4] '["My","Cool"]' -> 解析失败 (符合预期)
[5] '("My","Cool",' -> 解析失败 (符合预期)从上述测试结果可以看出,该语法成功地解析了所有预期的有效输入,并且最重要的是,它正确地拒绝了("My""Cool""Array")这类缺少逗号分隔符的非法输入。这表明我们的语法在解析阶段就提供了强大的错误检测能力。
通过精心设计的Parsimonious语法规则array = "(" string? (comma string?)* ")",我们成功地解决了解析包含空元素的逗号分隔字符串数组的挑战。这个语法不仅能够灵活地处理各种有效格式(包括空数组和带有空元素的数组),而且能够在解析阶段精确地识别并拒绝不规范的输入。这种方法提高了数据解析的鲁棒性,并简化了后续的数据处理流程,是构建可靠解析器的关键实践。
以上就是使用Parsimonious构建鲁棒的CSV风格字符串解析器的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号