以逗号分割字符串,但在括号环境中除外

时间:2014-11-07 19:44:47

标签: python regex parsing pyparsing

我想在逗号中拆分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.']

这涉及括号匹配,所以可能正则表达式在这里没有帮助。 PyParsingcommaSeparatedList 几乎执行我需要的操作,但引用的")环境受到保护而不是{{1} - 定界的。

任何提示?

3 个答案:

答案 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.']