我想这个问题已经足够基本了,答案肯定已经存在了,但我的谷歌傅技能肯定不够。
我需要使用以下格式解析字符串:upper:lower cc ; ! comment
。字符%
用于转义特殊字符%:; !
。:
字符将upper
与lower
分隔开来。;
字符终止一行。空格字符用于对cc
元素进行定界。使用!
介绍注释。以下字符串应按如下所示进行解析:
a:b c ; upper="a" lower="b" cc="c" comment=""
a%::b c ; upper="a:" lower="b" cc="c" comment=""
a%%:b c ; ! x upper="a%" lower="b" cc="c" comment=" x"
a%!:b c ; ! x upper="a!" lower="b" cc="c" comment=" x"
a%%%::b c ; upper="a%:" lower="b" cc="c" comment=""
在python中处理这项任务的最具python风格(即简单、可读、优雅(和健壮的方法是什么?正则表达式合适吗?
我尝试编写一个正则表达式,该表达式使用负后向查找来检测:
之前的奇数个%
s,但显然后向查找不能是可变长度的。
我认为regexp不能可靠地捕获转义状态。这是一个状态机风格的解析器。
def parse_line(s):
fields = [""]
in_escape = False
for i, c in enumerate(s):
if not in_escape:
if c == "%": # Start of escape
in_escape = True
continue
if (len(fields) == 1 and c == ":") or (len(fields) == 2 and c == " "): # Next field
fields.append("")
continue
if c == ";": # End-of-line
break
fields[-1] += c # Regular or escaped character
in_escape = False
return (fields, s[i + 1:])
print(parse_line("a:b c ;"))
print(parse_line("a%::b c ;"))
print(parse_line("a%%:b c ; ! x"))
print(parse_line("a%!:b c ; ! x"))
print(parse_line("a%%%::b c defgh:!:heh;"))
print(parse_line("a%;"))
print(parse_line("a%;:b!unterminated-line"))
输出
(['a', 'b', 'c '], '')
(['a:', 'b', 'c '], '')
(['a%', 'b', 'c '], ' ! x')
(['a!', 'b', 'c '], ' ! x')
(['a%:', 'b', 'c defgh:!:heh'], '')
(['a;'], '')
(['a;', 'b!unterminated-line'], '')
即retval是解析字段的2元组,以及;
标记之后的行的其余部分(其可以包含也可以不包含注释(。
类似于AKX的答案,但当我看到它时,我已经准备好了。此外,方法有点不同(更容易适应不同的格式(,结果可能也会稍微干净一些。
def parse(line):
parts = [""]
delims = ": ; !"
escape = False
for c in line:
if escape:
parts[-1] += c
escape = False
elif c == "%":
escape = True
elif c == delims[:1]:
parts += [""]
delims = delims[1:]
else:
parts[-1] += c
return [p for p in parts if p] if ";" not in delims else None
lines = ["a:b c ;","a%::b c ;","a%%:b c ; ! x","a%!:b c ; ! x","a%%%::b c ;","a:b incomplete"]
for line in lines:
print(line, "t", parse(line))
基本上,这会逐个字符地迭代行,跟踪"转义模式",并使用下一个预期的分隔符检查当前字符。
输出:
a:b c ; ['a', 'b', 'c']
a%::b c ; ['a:', 'b', 'c']
a%%:b c ; ! x ['a%', 'b', 'c', ' x']
a%!:b c ; ! x ['a!', 'b', 'c', ' x']
a%%%::b c ; ['a%:', 'b', 'c']
a:b incomplete None
根据@MichaelButscher的评论,我使用正则表达式编写了以下解决方案:
def parse_line(line):
parsed = re.match(r'''( (?: %. | [^:] )+ ) # capture upper
(?: : # colon delimiter
( (?: %. | [^ ] )+ ) # capture lower
)? # :lower is optional
+ # space delimiter(s)
( (?: %. | [^ ;] )+ ) # capture cont class
+; # space delimiter(s)
( .* ) s* $ # capture comment''',
line, re.X)
groups = parsed.groups(default='')
groups = [re.sub('%(.)', r'1', elem) for elem in groups] # unescape
return groups
这会产生以下结果:
>>> print(parse_line("a:b c ;"))
['a', 'b', 'c', '']
>>> print(parse_line("a%::b c ;"))
['a:', 'b', 'c', '']
>>> print(parse_line("a%%:b c ; ! x"))
['a%', 'b', 'c', ' ! x']
>>> print(parse_line("a%!:b c ; ! x"))
['a!', 'b', 'c', ' ! x']
格式不正确的条目返回NoneType
对象。