我想在逗号中拆分Python多行字符串,除非逗号位于括号内的表达式中。例如,字符串
{J. Doe, R. Starr}, {Lorem
{i}psum dolor }, Dol. sit., am. et.
应分成
['{J. Doe, R. Starr}', '{Lorem\n{i}psum dolor }', 'Dol. sit.', 'am. et.']
这涉及括号匹配,所以可能正则表达式在这里没有帮助。 PyParsing有commaSeparatedList
几乎执行我需要的操作,但引用的("
)环境受到保护而不是{{1} - 定界的。
任何提示?
答案 0 :(得分:12)
编写自己的自定义拆分功能:
input_string = """{J. Doe, R. Starr}, {Lorem
{i}psum dolor }, Dol. sit., am. et."""
expected = ['{J. Doe, R. Starr}', '{Lorem\n{i}psum dolor }', 'Dol. sit.', 'am. et.']
def split(s):
parts = []
bracket_level = 0
current = []
# trick to remove special-case of trailing chars
for c in (s + ","):
if c == "," and bracket_level == 0:
parts.append("".join(current))
current = []
else:
if c == "{":
bracket_level += 1
elif c == "}":
bracket_level -= 1
current.append(c)
return parts
assert split(input_string), expected
答案 1 :(得分:6)
在这种情况下,您可以使用re.split
:
>>> from re import split
>>> data = '''\
... {J. Doe, R. Starr}, {Lorem
... {i}psum dolor }, Dol. sit., am. et.'''
>>> split(',\s*(?![^{}]*\})', data)
['{J. Doe, R. Starr}', '{Lorem\n{i}psum dolor }', 'Dol. sit.', 'am. et.']
>>>
以下是正则表达式模式匹配的解释:
, # Matches ,
\s* # Matches zero or more whitespace characters
(?! # Starts a negative look-ahead assertion
[^{}]* # Matches zero or more characters that are not { or }
\} # Matches }
) # Closes the look-ahead assertion
答案 2 :(得分:3)
Lucas Trzesniewski's comment实际上可以在Python中使用PyPi regex module(我只是将带有编号的命名组替换为命名组以缩短它):
>>> import regex
>>> r = regex.compile(r'({(?:[^{}]++|\g<1>)*})(*SKIP)(*FAIL)|\s*,\s*')
>>> s = """{J. Doe, R. Starr}, {Lorem
{i}psum dolor }, Dol. sit., am. et."""
>>> print(r.split(s))
['{J. Doe, R. Starr}', None, '{Lorem\n{i}psum dolor }', None, 'Dol. sit.', None, 'am. et.']
模式 - ({(?:[^{}]++|\g<1>)*})(*SKIP)(*FAIL)
- 匹配{...{...{}...}...}
类似结构({
匹配{
,(?:[^{}]++|\g<1>)*
匹配0个出现的2个替代:1)任何除{
和}
以外的1 +个字符([^{}]++
),2)与整个({(?:[^{}]++|\g<1>)*})
子模式匹配的文本。 (*SKIP)(*FAIL)
动词使引擎省略匹配缓冲区中的整个匹配值,从而将索引移动到匹配结束并保留任何不返回(我们“跳过”我们匹配的内容)。
\s*,\s*
匹配用0+空格括起来的逗号。
出现None
值是因为第一个分支中的捕获组在第二个分支匹配时为空。我们需要在第一个替代分支中使用捕获组进行递归。要删除空元素,请使用理解:
>>> print([x for x in r.split(s) if x])
['{J. Doe, R. Starr}', '{Lorem\n{i}psum dolor }', 'Dol. sit.', 'am. et.']