如何分割字符串有' A / B / C(D / E)'格式?

时间:2017-09-26 03:38:06

标签: python regex string

我有一个字符串s1 = 'type1/type2/type3',因此我可以简单地将其拆分为s1.split('/'),然后获取['type1', 'type2', 'type3']

但是还有一些像s2 = 'type1/type2/type3(a/c)'这样的其他字符串,通过使用上面的方法,它会给['type1', 'type2', 'type3(a', 'c'],这不是我想要的,但['type1', 'type2', 'type3(a/c)']是首选。

我想知道如何使用正则表达式拆分这两种字符串格式。请帮我解决这个问题。

3 个答案:

答案 0 :(得分:1)

您可以使用基于正面的负面预测进行拆分:

>>> import re
>>> str = 'type1/type2/type3(a/c)'
>>> print re.split(r'/(?![^()]*\))', str)
['type1', 'type2', 'type3(a/c)']

RegEx Demo

这假设您有( ... )平衡,非嵌套且没有转义括号。

(?![^()]*\))是一个负面预测,如果我们找不到),那么我们前面有(但未匹配)/,从而导致匹配失败在(...)内。

答案 1 :(得分:0)

有一种技术可以使用更强大的regex实现中的功能。别担心,它与标准re模块向后兼容。基本思想也可以在标准re中使用,但它更加繁琐 - 我将在本答案结尾处概述stdlib模块的方法。

# pip install regex
import regex as re
s1 = 'type1/type2/type3'
s2 = 'type1/type2/type3(a/c)'
s3 = 'A/(B/C(D/E))/F'
s4 = 'A/(B/C(D/E))/F(a/c)'

以下是模式:

pat = r'\(.*?\)(*SKIP)(*FAIL)|/'

演示:

>>> re.split(pat, s1)
['type1', 'type2', 'type3']
>>> re.split(pat, s2)
['type1', 'type2', 'type3(a/c)']
>>> re.split(pat, s3)
['A', '(B/C(D/E))', 'F']
>>> re.split(pat, s4)
['A', '(B/C(D/E))', 'F(a/c)']

它是如何工作的?像这样阅读正则表达式:

blacklisted(*SKIP)(*FAIL)|matched

此模式首先丢弃包含在非贪婪的parens中的任何,即\(.*?\),以及我们使用(*SKIP)(*FAIL)功能的地方,而不是那里在stdlib re中。然后它匹配|右侧的左侧,即斜线。

正如我所提到的,该技术也可以在标准re中使用,但您必须使用捕获组。该模式需要一个围绕右侧斜线的捕获组:

pat_ = r'\(.*?\)|(/)'

第1组将设置为" good"火柴。所以迭代如下:

>>> for match in re.finditer(pat_, s):
...     if match[1] is not None:
...         print(match.start())

将打印出需要拆分的索引。然后以编程方式拆分字符串是微不足道的。实际上,您可以使用re.subre.split直接在regex中执行此操作,但只要在拥有索引后直接在Python代码中进行拆分,它就会更清晰,更容易。

答案 2 :(得分:0)

非正则表达式替代

我知道你标记了,但这些问题并不适合正则表达式。有许多棘手的边缘情况,边缘情况的失败模式通常会返回不正确的结果,而您更喜欢引发异常。

你必须选择两个邪恶中较小的一个:一个简单的正则表达式在奇怪的输入上行为不端,或者一个怪物正则表达式,除了正则表达式引擎本身以外,每个人都无法理解。

通过编写一个小的解析器来跟踪你是否包含在parens中,通常会更容易。编写简单,维护简单。

这是一个基于解析器的解决方案和一系列测试,可能会使任何基于正则表达式的方法绊倒。这也将检测问题何时受到限制(不平衡的parens),并在必要时提高。

def fancy_split(s):
    parts, accumulator, nesting_level = [], [], 0
    for char in s:
        if char == '/':
            if nesting_level == 0:
                parts.append(''.join(accumulator))
                accumulator = []
                continue
        accumulator.append(char)
        if char == '(':
            nesting_level += 1
        elif char == ')':
            nesting_level -= 1
            if nesting_level < 0:
                raise Exception('unbalanced parens')
    parts.append(''.join(accumulator))
    if nesting_level != 0:
        raise Exception('unbalanced parens')
    assert '/'.join(parts) == s
    return parts

tests = {
    'type1/type2/type3': ['type1', 'type2', 'type3'],
    'type1/type2/type3(a/c)': ['type1', 'type2', 'type3(a/c)'],
    'A/(B/C(D/E))/F': ['A', '(B/C(D/E))', 'F'],
    'A/(B/C(D/E))/F(a/c)': ['A', '(B/C(D/E))', 'F(a/c)'],
    'A/((a/c)D/E)': ['A', '((a/c)D/E)'],
    'A': ['A'],
    'A/': ['A', ''],
    '/A': ['', 'A'],
    '': [''],
    '/': ['', ''],
    '//': ['', '', '']
}

for input_, expected_output in tests.items():
    assert fancy_split(input_) == expected_output