如何解析字符串并返回嵌套数组?
Posted
技术标签:
【中文标题】如何解析字符串并返回嵌套数组?【英文标题】:How to parse a string and return a nested array? 【发布时间】:2013-06-13 00:19:03 【问题描述】:我想要一个 Python 函数,它接受一个字符串并返回一个数组,其中数组中的每个项目要么是一个字符,要么是另一个此类数组。嵌套数组在输入字符串中以 '(' 开头并以 ')' 结尾进行标记。
因此,函数的行为如下:
1) foo("abc") == ["a", "b", "c"]
2) foo("a(b)c") == ["a", ["b"], "c"]
3) foo("a(b(c))") == ["a", ["b", ["c"]]]
4) foo("a(b(c)") == error: closing bracket is missing
5) foo("a(b))c") == error: opening bracket is missing
6) foo("a)b(c") == error: opening bracket is missing
注意:我更喜欢纯功能性的解决方案。
【问题讨论】:
在这里使用递归,非常合适。在令牌流中找到“(”意味着递归。在***调用中找到“)”意味着存在平衡不匹配。 确实是递归。只要字符串不太长,就是这样。还请记住,这可能需要一个包装函数,因为我们需要检查 '(' 的数量是否与 ')' 的数量匹配。如果是这样,请调用递归函数。如果不是,请给出适当的错误消息。 是的,我知道我会在这里使用递归。我知道如何编写一个函数(使用堆栈)来返回字符串中的括号是否平衡且有序,但它正在采取下一步并试图返回让我难过的嵌套数组。这不是家庭作业。这是一个更大的解析程序的一部分。 【参考方案1】:def foo(s):
def foo_helper(level=0):
try:
token = next(tokens)
except StopIteration:
if level != 0:
raise Exception('missing closing paren')
else:
return []
if token == ')':
if level == 0:
raise Exception('missing opening paren')
else:
return []
elif token == '(':
return [foo_helper(level+1)] + foo_helper(level)
else:
return [token] + foo_helper(level)
tokens = iter(s)
return foo_helper()
还有,
>>> foo('a((b(c))d)(e)')
['a', [['b', ['c']], 'd'], ['e']]
【讨论】:
你想尝试用函数式风格重写这个吗? @FinalZero 你是什么意思?这已经是一个递归解决方案了。 我的意思是不使用iter
和next
。
答案不是纯函数式的,但无论如何我都会接受它,因为它帮助我概念化和编写纯函数式解决方案,这需要foo
返回一个元组而不仅仅是一个数组.【参考方案2】:
迭代。
def foo(xs):
stack = [[]]
for x in xs:
if x == '(':
stack[-1].append([])
stack.append(stack[-1][-1])
elif x == ')':
stack.pop()
if not stack:
return 'error: opening bracket is missing'
#raise ValueError('error: opening bracket is missing')
else:
stack[-1].append(x)
if len(stack) > 1:
return 'error: closing bracket is missing'
#raise ValueError('error: closing bracket is missing')
return stack.pop()
assert foo("abc") == ["a", "b", "c"]
assert foo("a(b)c") == ["a", ["b"], "c"]
assert foo("a(b(c))") == ["a", ["b", ["c"]]]
assert foo("a((b(c))d)(e)") == ['a', [['b', ['c']], 'd'], ['e']]
assert foo("a(b(c)") == "error: closing bracket is missing"
assert foo("a(b))c") == "error: opening bracket is missing"
assert foo("a)b(c") == 'error: opening bracket is missing'
【讨论】:
+1 你不需要result
:只需设置stack = [[]]
,然后返回stack.pop()
。这样看起来更纯粹——只是堆栈。【参考方案3】:
我建议两种方法:
要么编写你自己的递归下降解析器,比如here,要么使用pyparsing,比如
import pyparsing as pp
expr = pp.Forward()
expr << pp.Word(pp.alphas) + pp.Optional('(' + expr + ')') + pp.Optional(pp.Word(pp.alphas))
在这里,您将递归表达式描述为一系列 alpha,可以通过平衡括号进行交错。当您检查此示例的输出时,您将看到如何获得所需的输出结构(尽管这需要您进行一些调整并需要了解一些有关 pyparsing 的知识)。
问候 标记
【讨论】:
【参考方案4】:使用regex
和ast.literal_eval
>>> import re
>>> from ast import literal_eval
>>> def listit(t):
... return list(map(listit, t)) if isinstance(t, (list, tuple)) else t
...
def solve(strs):
s = re.sub(r'[A-Za-z]', "'\g<0>',", strs)
s = re.sub(r"\)", "\g<0>,", s)
try: return listit( literal_eval('[' + s + ']') )
except : return "Invalid string! "
...
>>> solve("abc")
['a', 'b', 'c']
>>> solve("a(b)c")
['a', ['b'], 'c']
>>> solve("a(b(c))")
['a', ['b', ['c']]]
>>> solve("a(b(c)")
'Invalid string! '
>>> solve("a)b(c")
'Invalid string! '
>>> solve("a(b))c")
'Invalid string! '
>>> solve('a((b(c))d)(e)')
['a', [['b', ['c']], 'd'], ['e']]
【讨论】:
【参考方案5】:一种相当快速和讨厌的方法(只是为了不同的东西):
import json, re
def foo(x):
# Split continuous strings
# Match consecutive characters
matches = re.findall('[a-z]2,', x)
for m in matches:
# Join with ","
x = x.replace(m, '","'.join(y for y in list(m)))
# Turn curvy brackets into square brackets
x = x.replace(')', '"],"')
x = x.replace('(', '",["')
# Wrap whole string with square brackets
x = '["'+x+'"]'
# Remove empty entries
x = x.replace('"",', '')
x = x.replace(',""', '')
try:
# Load with JSON
return json.loads(x)
except:
# TODO determine error type
return "error"
def main():
print foo("abc") # ['a', 'b', 'c']
print foo("a(b)c") # ['a', ['b'], 'c']
print foo("a(b(c))") # ['a', ['b', ['c']]]
print foo("a(b))c") # error
print foo('a((b(c))d)(e)') # ['a', [['b', ['c']], 'd'], ['e']]
【讨论】:
【参考方案6】:def parse_nested(iterator, level=0):
result = []
for c in iterator:
if c == '(':
result.append(parse_nested(iterator, level+1))
elif c == ')':
if level:
return result
else:
raise ValueError("Opening parenthesis missing")
else:
result.append(c)
if level:
raise ValueError("Closing parenthesis missing")
else:
return result
print parse_nested(iter('a((b(c))d)(e)'))
【讨论】:
【参考方案7】:递归是非常强大的东西,你应该尝试使用它。
这是我的代码:
# encoding: utf-8
# Python33
def check(s):
cs = [c for c in s if c == '(' or c ==')']
flag = 0
for c in cs:
if flag < 0:
return 'opening bracket is missing'
if c == '(':
flag += 1
else:
flag -= 1
if flag < 0:
return 'opening bracket is missing'
elif flag > 0:
return 'closing bracket is missing'
else:
return ''
def _foo(cs):
result = []
while len(cs):
c = cs.pop(0)
if c == '(':
result.append(_foo(cs))
elif c == ')':
return result
else:
result.append(c)
return result
def foo(s):
valiad = check(s)
if valiad:
return valiad
cs = list(s)
return _foo(cs)
if __name__ == '__main__':
ss = ["abc","a(b)c","a(b(c))","a(b(c)","a(b))c","a)b(c"]
for s in ss:
print(foo(s))
【讨论】:
我的代码没问题,但在答案区域显示时出错...我正在尝试修复它。_______我替换了 '以上是关于如何解析字符串并返回嵌套数组?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用json方法解析android中没有键名的数组内的嵌套json编码数组?
如何从通过 Moya.Response 查询返回的对象解析嵌套的 JSON 数组
Javascript - 解析 html 字符串获取具有类 X 的每个元素并返回它们的数组