我有一个字符串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)']
是首选。
我想知道如何使用正则表达式拆分这两种字符串格式。请帮我解决这个问题。
答案 0 :(得分:1)
您可以使用基于正面的负面预测进行拆分:
>>> import re
>>> str = 'type1/type2/type3(a/c)'
>>> print re.split(r'/(?![^()]*\))', str)
['type1', 'type2', 'type3(a/c)']
这假设您有( ... )
平衡,非嵌套且没有转义括号。
(?![^()]*\))
是一个负面预测,如果我们找不到)
,那么我们前面有(
但未匹配)
或/
,从而导致匹配失败在(...)
内。
答案 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.sub
和re.split
直接在regex中执行此操作,但只要在拥有索引后直接在Python代码中进行拆分,它就会更清晰,更容易。
答案 2 :(得分:0)
我知道你标记了regex,但这些问题并不适合正则表达式。有许多棘手的边缘情况,边缘情况的失败模式通常会返回不正确的结果,而您更喜欢引发异常。
你必须选择两个邪恶中较小的一个:一个简单的正则表达式在奇怪的输入上行为不端,或者一个怪物正则表达式,除了正则表达式引擎本身以外,每个人都无法理解。
通过编写一个小的解析器来跟踪你是否包含在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