我的格式为'AB(AB(DDC)C)A(BAAC)DAB(ABC)'
。
A
,B
,C
或D
)。例如,拥有'AB(AB(DDC)C)A(BAAC)DA'
,最高级别为 AB (AB(DDC)C) A (BAAC) DA - > [A, B, A, D, A]
和相应的子项为[None, AB(DDC)C, BAAC, None, None]
。孩子们要以递归方式解析。
我在这里实施了一个解决方案:
def parse_string(string):
i = 0
parsed = []
while i < len(string):
if string[i] in ('A', 'B', 'C', 'D'):
parsed.append([string[i], None])
i += 1
elif string[i] == '(':
open_brakets = 1
i += 1
j = i
while open_brakets:
if string[j] == '(':
open_brakets += 1
elif string[j] == ')':
open_brakets -= 1
j += 1
# Parse the children as well
parsed[-1][-1] = parse_string(string[i:j - 1])
i = j
else:
i += 1
return parsed
print parse_string('AB(AB(DDC)C)A(BAAC)DAB(ABC)')
虽然我觉得它有点难看,但我确信效率不高。
我想知道是否有办法用更干净/更快/更优雅的方式使用Python?允许使用外部库(特别是如果它们是用C!:-P编写的话)。
应该有效的字符串的其他示例:
ABC(DAB(ACB)BBB(AAA)ABC)DCB
一般来说,字符串的长度不受限制,既不是儿童的数量,也不是长度,也不是嵌套级别的数量。
答案 0 :(得分:3)
如果你需要递归地解析内括号:
def parse_tree(tree, string, start=0):
index = start
while index < len(string):
current = string[index]
if current == "(":
child = tree[-1][1]
child_parsed = parse_tree(child, string, index+1)
index += child_parsed + 2 # adds 2 for the parentheses
elif current == ")":
break
else:
tree.append((current, []))
index += 1
return index - start
tree = []
print(parse_tree(tree, 'abc(abc(defg)d)de(f)gh'))
这种工作方式可以被认为是状态机。状态机接受节点定义,直到它看到开括号,其中它将新的上下文(即递归函数调用)推送到解析堆栈以解析括号的内容。解析内部上下文时,紧括的括号会弹出上下文。
如果你有更复杂的语法,可以更好地扩展的另一个选择是使用像PyParsing这样的解析库:
from pyparsing import OneOrMore, Optional, oneOf, alphas, Word, Group, Forward, Suppress, Dict
# define the grammar
nodes = Forward()
nodeName = oneOf(list(alphas))
nodeChildren = Suppress('(') + Group(nodes) + Suppress( ')')
node = Group(nodeName + Optional(nodeChildren))
nodes <<= OneOrMore(node)
print(nodes.parseString('abc(abc(defg)d)de(f)gh'))
解析像PyParsing这样的库可以让你定义一个易于阅读的声明语法。
回答原始的非递归解析:一种方法是使用itertools(累积只来自Python 3.2及更高版本,itertools文档有pure python implementation of accumulate用于较旧的版本)。这避免了使用索引:
from itertools import takewhile, accumulate
PARENS_MAP = {'(': 1, ')': -1}
def parse_tree(tree, string):
string = iter(string)
while string:
current = next(string)
if current == "(":
child = iter(string)
child = ((c, PARENS_MAP.get(c, 0)) for c in child)
child = accumulate(child, lambda a,b: (b[0], a[1]+b[1]))
child = takewhile(lambda c: c[1] >= 0, child)
child = (c[0] for c in child)
tree[-1][1] = "".join(child)
else:
tree.append([current, None])
print(parse_tree('abc(abc(defg)d)de(f)gh'))
我不太确定它是更快还是更优雅,但我认为使用显式索引更容易编写,理解和修改。
答案 1 :(得分:1)
您可以使用regex
来解析文字。
作为更通用的字符串,请考虑以下字符串:
>>> s ='AB(AB(DDC)C)A(BAAC)DAB(ABC)DDD'
您可以使用re.findall
查找外部模式:
>>> re.findall(r'(?<=\))\w+(?=\(|$)|^\w+(?=\()',s)
['AB', 'A', 'DAB', 'DDD']
使用带有re.split
的正则表达式来获取括号内的字符串:
>>> re.split(r'(?<=\))\w+(?=\(|$)|^\w+(?=\()',s)
['', '(AB(DDC)C)', '(BAAC)', '(ABC)', '']
有关前面的正则表达式的解释:
这个正则表达式包含2个与pip标记(|
)连接的部分,用作逻辑or
:
(?<=\))\w+(?=\(|$)
:此正则表达式将匹配前面加\w+
后跟)
或(
的单词字符($
)的任意组合,即$是字符串修饰符的结尾匹配字符串的结尾。
$
的 注意适用于案例DDD
!
^\w+(?=\()
:此正则表达式将匹配字符串开头出现的任何单词字符组合(修饰符^
将匹配字符串的开头),然后是(
答案 2 :(得分:0)
至于有点丑陋,这是在旁观者的眼中。
就速度而言,很难改进代码。
ADDED:我将在C ++中使用它。如果你愿意,你可以适应Python。
这显示了如何使用递归来完成它。
顶级函数是topLevel("blah blah")
。
bool seeLetter(char* &s){
if (*s=='A' || *s=='B' || *s=='C' || *s=='D'){
s++;
return true;
} else {
return false;
}
}
bool seeChar(char* &s, char c){
if (*s == c){s++; return true;}
return false;
}
bool seeList(char* &s){
while(*s){
if (seeLetter(s)){
} else if (seeChar(s, '(')){
if (!seeList(s)) return false;
if (!seeChar(s, ')')) return false;
} else break;
}
return true;
}
bool topLevel(char* &s){
if (!seeList(s)) return false;
return (*s == '\0'); // check for garbage at end
}