我有一串这样的格式:
"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', '']
这里的奇怪元素是演员姓名,甚至是角色。然后我可以用逗号分割名称,并以某种方式提取名称 - 角色对。但这似乎比我的第一种做法更糟糕。
有没有更容易/更好的方法来做到这一点,使用单个正则表达式还是一段好的代码?
答案 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)']
上面的正则表达式匹配一个或多个:
关于这种方法的一个怪癖是相邻的分离器被视为单个分离器。也就是说,你不会看到一个空字符串。根据您的使用情况,这可能是错误或功能。
另请注意,正则表达式不适用于可能嵌套的情况。因此,例如,这将错误地分开:
"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
并在模块级别定义level
和qtflags
。
以下是它的使用方法:
>>> 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)
如果您的数据中存在任何错误或噪音,则上述答案都不正确。
如果您每次都知道数据是正确的,那么很容易找到一个好的解决方案。但是如果存在格式错误会发生什么?你想发生什么?
假设有嵌套括号?假设有无与伦比的括号?假设字符串以逗号结尾或以逗号开头,或者连续两行?
以上所有解决方案都会产生或多或少的垃圾,而不会向您报告。
对我而言,我首先要严格限制“正确”的数据 - 没有嵌套括号,没有不匹配的括号,并且在评论之前,之间或之后都没有空段 - 我去了验证,如果我无法验证,则提出异常。