Python中的正则表达式花括号

3

我有一个这样的字符串:

a = '{CGPoint={CGPoint=d{CGPoint=dd}}}{CGSize=dd}dd{CSize=aa}'

目前我正在使用这个re语句来获得所需的结果:

filter(None, re.split("\\{(.*?)\\}", a))

但这会给我带来:
['CGPoint={CGPoint=d{CGPoint=dd', '}}', 'CGSize=dd', 'dd', 'CSize=aa']

这个列表与我的当前情况不符,我需要像这样的一个列表:
['CGPoint={CGPoint=d{CGPoint=dd}}', 'CGSize=dd', 'dd', 'CSize=aa']

6
Python正则表达式不支持此操作,无法处理任意嵌套结构。需要逐个字符遍历字符串并维护一个“深度”计数器,在遇到{时增加,在遇到}时减少。当深度返回到0时,可以取从第一个{处到此处的子字符串。 - Martin Ender
3
@m.buettner是正确的。你需要编写一个解析器来解析令牌,即开放和闭合括号,以生成你的结果。鉴于问题的简单性,这应该很容易做到。 - Daniel Gimenez
我原以为Python可以帮我做到这一点:D,但是是的,写一个解析器也不会有任何问题,谢谢:) - user232343
确切的想法是什么,我不完全看得到你想要得到的结果的逻辑。虽然你对正则表达式有递归反向引用,可能是可以实现的,但我仍然不完全理解你想要什么? - gitaarik
4个回答

4
正如@m.buettner在评论中指出的那样,Python的正则表达式实现无法匹配嵌套到任意程度的符号对。(其他语言可以,特别是当前版本的Perl。)当您有无法解析的文本时,Python的正确做法是使用递归下降解析器。
然而,没有必要通过编写自己的解析器来重新发明轮子;有许多易于使用的解析库可供选择。我推荐使用pyparsing,它允许您直接在代码中定义语法,并轻松地将操作附加到匹配的令牌。您的代码将类似于这样:
import pyparsing

lbrace = Literal('{')
rbrace = Literal('}')  
contents = Word(printables)
expr = Forward()
expr << Combine(Suppress(lbrace) + contents + Suppress(rbrace) + expr)

for line in lines:
    results = expr.parseString(line)

2

我非常喜欢 Python 中的一种替代正则表达式模块,它支持递归模式: https://pypi.python.org/pypi/regex

pip install regex

然后你可以在正则表达式中使用递归模式,如下示例脚本所示:

import regex
from pprint import pprint


thestr = '{CGPoint={CGPoint=d{CGPoint=dd}}}{CGSize=dd}dd{CSize=aa}'

theregex = r'''
    (
        {
            (?<match>
                [^{}]*
                (?:
                    (?1)
                    [^{}]*
                )+
                |
                [^{}]+
            )
        }
        |
        (?<match>
            [^{}]+
        )
    )
'''

matches = regex.findall(theregex, thestr, regex.X)

print 'all matches:\n'
pprint(matches)

print '\ndesired matches:\n'
print [match[1] for match in matches]

这将输出:
all matches:

[('{CGPoint={CGPoint=d{CGPoint=dd}}}', 'CGPoint={CGPoint=d{CGPoint=dd}}'),
 ('{CGSize=dd}', 'CGSize=dd'),
 ('dd', 'dd'),
 ('{CSize=aa}', 'CSize=aa')]

desired matches:

['CGPoint={CGPoint=d{CGPoint=dd}}', 'CGSize=dd', 'dd', 'CSize=aa']

有没有办法让“theregex”匹配内部花括号字符串,“{CGPoint=d{CGPoint=dd}}”和“{CGPoint=dd}”? - unutbu
@unutbu 可能有某种方式可以实现,但是从正则表达式返回的匹配结果中,您无法知道嵌套括号的深度。如果您想要知道深度,您提供的解决方案会更好。 - gitaarik

2

pyparsing 提供了一个 nestedExpr 函数用于匹配嵌套表达式:

import pyparsing as pp

ident = pp.Word(pp.alphanums)
expr = pp.nestedExpr("{", "}") | ident

thestr = '{CGPoint={CGPoint=d{CGPoint=dd}}}{CGSize=dd}dd{CSize=aa}'
for result in expr.searchString(thestr):
    print(result)

产出
[['CGPoint=', ['CGPoint=d', ['CGPoint=dd']]]]
[['CGSize=dd']]
['dd']
[['CSize=aa']]

1
这里是一些伪代码。它创建了一个字符串的栈,并在遇到闭合括号时弹出它们。还有一些额外的逻辑来处理第一个遇到的括号没有包含在数组中这个事实。
String source = "{CGPoint={CGPoint=d{CGPoint=dd}}}{CGSize=dd}dd{CSize=aa}";
Array results;
Stack stack;

foreach (match in source.match("[{}]|[^{}]+")) {
    switch (match) {
        case '{':
           if (stack.size == 0) stack.push(new String()); // add new empty string
           else stack.push('{'); // child, so include matched brace.
        case '}':
           if (stack.size == 1) results.add(stack.pop()) // clear stack add to array
           else stack.last += stack.pop() + '}"; // pop from stack and concatenate to previous
        default:
           if (stack.size == 0) results.add(match); // loose text, add to results
           else stack.last += match;  // append to latest member.
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接