如何用括号外的逗号分割字符串?

时间:2009-10-30 08:02:10

标签: python regex split

我有一串这样的格式:

"Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"

基本上它是演员姓名的列表(可选地后面跟着他们在括号中的作用)。角色本身可以包含逗号(演员姓名不能,我强烈希望如此)。

我的目标是将此字符串拆分为成对列表 - (actor name, actor role)

一个明显的解决方案是遍历每个角色,检查'('')'','的出现情况,并在外部发生逗号时将其拆分。但这似乎有点沉重......

我正在考虑使用正则表达式来拆分它:首先用括号分割字符串:

import re
x = "Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
s = re.split(r'[()]', x) 
# ['Wilbur Smith ', 'Billy, son of John', ', Eddie Murphy ', 'John', ', Elvis Presley, Jane Doe ', 'Jane Doe', '']

这里的奇怪元素是演员姓名,甚至是角色。然后我可以用逗号分割名称,并以某种方式提取名称 - 角色对。但这似乎比我的第一种做法更糟糕。

有没有更容易/更好的方法来做到这一点,使用单个正则表达式还是一段好的代码?

10 个答案:

答案 0 :(得分:19)

一种方法是将findall与正则表达式一起使用,该正则表达式可以在分隔符之间进行贪婪匹配。例如:

>>> s = "Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
>>> r = re.compile(r'(?:[^,(]|\([^)]*\))+')
>>> r.findall(s)
['Wilbur Smith (Billy, son of John)', ' Eddie Murphy (John)', ' Elvis Presley', ' Jane Doe (Jane Doe)']

上面的正则表达式匹配一个或多个:

  • 非逗号,非开放式字符
  • 以open paren开头的字符串,包含0个或更多非close-parens,然后是close paren

关于这种方法的一个怪癖是相邻的分离器被视为单个分离器。也就是说,你不会看到一个空字符串。根据您的使用情况,这可能是错误或功能。

另请注意,正则表达式适用于可能嵌套的情况。因此,例如,这将错误地分开:

"Wilbur Smith (son of John (Johnny, son of James), aka Billy), Eddie Murphy (John)"

如果你需要处理嵌套,那么最好的选择就是将字符串分成parens,逗号和everthing else(基本上将它标记 - 这部分仍然可以用正则表达式完成),然后遍历那些重新组装的标记。字段,随时跟踪您的嵌套级别(这跟踪嵌套级别是正则表达式无法自行完成的。)

答案 1 :(得分:5)

我认为解决这个问题的最佳方法是使用python的内置csv模块。

由于csv模块只有allows一个字符quotechar,您需要对输入进行替换,以便将()转换为|或{{ 1}}。然后确保使用适当的方言然后离开。

答案 2 :(得分:5)

s = re.split(r',\s*(?=[^)]*(?:\(|$))', x) 

前瞻符合下一个左括号或字符串末尾的所有内容, iff 之间没有紧密括号。这可以确保逗号不在括号内。

答案 3 :(得分:2)

尝试使用人类可读的正则表达式:

import re

regex = re.compile(r"""
    # name starts and ends on word boundary
    # no '(' or commas in the name
    (?P<name>\b[^(,]+\b)
    \s*
    # everything inside parentheses is a role
    (?:\(
      (?P<role>[^)]+)
    \))? # role is optional
    """, re.VERBOSE)

s = ("Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley,"
     "Jane Doe (Jane Doe)")
print re.findall(regex, s)

输出:

[('Wilbur Smith', 'Billy, son of John'), ('Eddie Murphy', 'John'), 
 ('Elvis Presley', ''), ('Jane Doe', 'Jane Doe')]

答案 4 :(得分:1)

我的回答不会使用正则表达式。

我认为状态为“in_actor_name”的简单字符扫描程序应该可以工作。请记住,状态“in_actor_name”在此状态下由')'或逗号终止。

我的尝试:

s = 'Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)'

in_actor_name = 1
role = ''
name = ''
for c in s:
    if c == ')' or (c == ',' and in_actor_name):
        in_actor_name = 1
        name = name.strip()
        if name:
            print "%s: %s" % (name, role)
        name = ''
        role = ''
    elif c == '(':
        in_actor_name = 0
    else:
        if in_actor_name:
            name += c
        else:
            role += c
if name:
    print "%s: %s" % (name, role)

输出:

Wilbur Smith: Billy, son of John
Eddie Murphy: John
Elvis Presley: 
Jane Doe: Jane Doe

答案 5 :(得分:1)

这是我过去用于此类案例的一般技术:

使用sub模块的re函数作为替换参数。该功能可以跟踪打开和关闭的支架,支架和支架,以及单引号和双引号,并且仅在这种括号和引用的子串之外执行替换。然后,您可以用另一个您确定不会出现在字符串中的字符替换非括号/引用的逗号(我使用ASCII / Unicode group-separator:chr(29)代码),然后执行一个简单的字符串。拆分那个角色。这是代码:

import re
def srchrepl(srch, repl, string):
    """Replace non-bracketed/quoted occurrences of srch with repl in string"""

    resrchrepl = re.compile(r"""(?P<lbrkt>[([{])|(?P<quote>['"])|(?P<sep>["""
                            + srch + """])|(?P<rbrkt>[)\]}])""")
    return resrchrepl.sub(_subfact(repl), string)

def _subfact(repl):
    """Replacement function factory for regex sub method in srchrepl."""
    level = 0
    qtflags = 0
    def subf(mo):
        nonlocal level, qtflags
        sepfound = mo.group('sep')
        if  sepfound:
            if level == 0 and qtflags == 0:
                return repl
            else:
                return mo.group(0)
        elif mo.group('lbrkt'):
            level += 1
            return mo.group(0)
        elif mo.group('quote') == "'":
            qtflags ^= 1            # toggle bit 1
            return "'"
        elif mo.group('quote') == '"':
            qtflags ^= 2            # toggle bit 2
            return '"'
        elif mo.group('rbrkt'):
            level -= 1
            return mo.group(0)
    return subf

如果您的Python版本中没有nonlocal,只需将其更改为global并在模块级别定义levelqtflags

以下是它的使用方法:

>>> GRPSEP = chr(29)
>>> string = "Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
>>> lst = srchrepl(',', GRPSEP, string).split(GRPSEP)
>>> lst
['Wilbur Smith (Billy, son of John)', ' Eddie Murphy (John)', ' Elvis Presley', ' Jane Doe (Jane Doe)']

答案 6 :(得分:1)

这篇文章给了我很多帮助。我希望用引号外的逗号分隔一个字符串。我用这个作为首发。我的最后一行代码是regEx = re.compile(r'(?:[^,"]|"[^"]*")+')这就是诀窍。非常感谢。

答案 7 :(得分:0)

我当然同意上面的@Wogan,使用CSV模块是一种很好的方法。话虽如此,如果您仍想尝试正则表达式解决方案,请尝试一下,但您必须将其改编为Python方言

string.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/)

HTH

答案 8 :(得分:0)

拆分“)”

>>> s="Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
>>> s.split(")")
['Wilbur Smith (Billy, son of John', ', Eddie Murphy (John', ', Elvis Presley, Jane Doe (Jane Doe', '']
>>> for i in s.split(")"):
...   print i.split("(")
...
['Wilbur Smith ', 'Billy, son of John']
[', Eddie Murphy ', 'John']
[', Elvis Presley, Jane Doe ', 'Jane Doe']
['']

您可以进一步检查以获取那些未附带的名称()。

答案 9 :(得分:-1)

如果您的数据中存在任何错误或噪音,则上述答案都不正确。

如果您每次都知道数据是正确的,那么很容易找到一个好的解决方案。但是如果存在格式错误会发生什么?你想发生什么?

假设有嵌套括号?假设有无与伦比的括号?假设字符串以逗号结尾或以逗号开头,或者连续两行?

以上所有解决方案都会产生或多或少的垃圾,而不会向您报告。

对我而言,我首先要严格限制“正确”的数据 - 没有嵌套括号,没有不匹配的括号,并且在评论之前,之间或之后都没有空段 - 我去了验证,如果我无法验证,则提出异常。