我得到了这样的格式的字符串:
1"Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
因此从根本上讲,它是演员姓名的列表(可选地,其后是括号中的角色)。 角色本身可以包含逗号(演员的名字不能,我强烈希望如此)。
我的目标是将此字符串拆分为成对的列表-(actor name, actor role)。
一个显而易见的解决方案是遍历每个字符,检查'(',')'和','的出现情况,并在出现逗号时将其拆分。 但这似乎有点沉重...
我正在考虑使用正则表达式拆分它:首先用括号将字符串拆分:
1
2
3
4import 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', '']
这里的奇数元素是演员名称,甚至是角色。 然后我可以用逗号分隔名称,并以某种方式提取名称角色对。 但是,这似乎比我的第一种方法还要糟糕。
是否有任何更简单/更好的方法来执行此操作,而只需一个正则表达式或一段不错的代码?
一种方法是将findall与正则表达式一起使用,该正则表达式贪婪地匹配分隔符之间可能会发生的事情。例如:
1
2
3
4>>> 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)']
上面的正则表达式匹配一个或多个:
非逗号,非开放字符
以开放括号开头的字符串,包含0个或多个非封闭括号,然后是封闭括号
关于此方法的一个怪癖是将相邻的分隔符视为单个分隔符。也就是说,您不会看到一个空字符串。根据您的用例,这可能是错误或功能。
还要注意,正则表达式不适用于可能嵌套的情况。因此,例如,这将错误地拆分:
1"Wilbur Smith (son of John (Johnny, son of James), aka Billy), Eddie Murphy (John)"
如果您需要处理嵌套,则最好的选择是将字符串分成括号,逗号和其他所有内容(本质上是对其进行标记化-这部分仍可以使用正则表达式来完成),然后遍历这些标记重新组合字段,并保持跟踪嵌套级别(正则表达式无法自行执行的嵌套级别跟踪)。
您可以通过匹配记录而不是分隔符立即将其分为几个字段:[(m.group(" name"),m.group(" role")))for re.findall("(?P 。+ ?)(?(?P [^] +)(, s * | $))",x)]
如果需要,可以为令牌解决方案+1。上下行走时弹出堆栈,这是一种经典的方法。
每当我看到正则表达式有用时,就像我这样,我就开始怀疑-它们是否是人类可读的?还是它只是我...乍看之下谁看不见?
好答案。谢谢劳伦斯!如果我想这样做但不在输出中括号内包含内容,该如何解决这个问题?
@ user815423426在这种情况下,Id可能使用的方法是对结果列表进行第二次传递并删除括号。例如:r = [re.sub(r\([^)]*\), , s) for s in r]之类的东西。如果您需要更详细的答案,则可能应该发布一个单独的问题。
1s = re.split(r',\s*(?=[^)]*(?:\(|$))', x)
先行匹配下一个开括号或字符串末尾的所有内容,前提是两者之间没有闭括号。这样可以确保逗号不在括号内。
我认为解决此问题的最佳方法是使用python内置的csv模块。
由于csv模块仅允许一个字符quotechar,因此您需要对输入进行替换以将()转换为类似|或"的字符。然后确保您使用的是适当的方言,然后离开。
尝试人类可读的正则表达式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import re
regex = re.compile(r"""
# name starts and ends on word boundary
# no '(' or commas in the name
(?P\b[^(,]+\b)
\s*
# everything inside parentheses is a role
(?:\(
(?P[^)]+)
\))? # 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)
输出:
1
2[('Wilbur Smith', 'Billy, son of John'), ('Eddie Murphy', 'John'),
('Elvis Presley', ''), ('Jane Doe', 'Jane Doe')]
可读的正则表达式-是不是矛盾词?
这篇文章对我有很大帮助。 我想用引号外的逗号分隔字符串。 我用它作为入门。 我的最后一行代码是regEx = re.compile(r'(?:[^,"]|"[^"]*")+')。 万分感谢。
这是我过去用于此类情况的通用技术:
将re模块的sub函数与函数作为替换参数一起使用。该函数跟踪打开和关闭括号,方括号和花括号以及单引号和双引号,并且仅在此类带括号和引号的子字符串之外执行替换。然后,您可以用您确定不会出现在字符串中的另一个字符替换无括号/带引号的逗号(我使用ASCII / Unicode组分隔符:chr(29)代码),然后执行一个简单的字符串。在那个角色上分裂。这是代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33import re
def srchrepl(srch, repl, string):
"""Replace non-bracketed/quoted occurrences of srch with repl in string"""
resrchrepl = re.compile(r"""(?P[([{])|(?P['"])|(?P["""
+ srch +"""])|(?P[)\]}])""")
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。
使用方法如下:
1
2
3
4
5>>> 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)']
我的答案将不使用正则表达式。
我认为状态为in_actor_name的简单字符扫描程序应该可以工作。请记住,状态" in_actor_name"在此状态下以')'或逗号终止。
我的尝试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22s = '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)
输出:
1
2
3
4Wilbur Smith: Billy, son of John
Eddie Murphy: John
Elvis Presley:
Jane Doe: Jane Doe
被")"分割
1
2
3
4
5
6
7
8
9
10>>> 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']
['']
您可以进一步检查以获取()不附带的那些名称。
我当然同意以上@Wogan的观点,即使用CSV模块是一个好方法。话虽如此,如果您仍然想尝试使用正则表达式解决方案,请尝试一下,但是您必须将其适应Python方言
1string.split(/,(?=(?:[^"]*"[^"]*")*(?![^"]*"))/)
高温超导
如果您的数据中有任何错误或干扰,以上答案均不正确。
如果您每次都知道数据正确,那么很容易想到一个好的解决方案。但是,如果出现格式错误怎么办?你想发生什么?
假设有嵌套括号?假设括号不匹配?假设字符串以逗号结尾或以逗号开头,或者连续有两个?
以上所有解决方案都会产生或多或少的垃圾,并且不会向您报告。
由我自己决定,我将严格限制什么是"正确的"数据-在注释之前,之间或之后,没有嵌套括号,没有不匹配的括号,没有空段-随便验证,然后提出如果我无法验证,则为例外。
我们必须假设该问题包含我们需要回答的所有信息。因此,我们假设输入已经过验证,并且格式已经完整描述(例如,没有嵌套的括号)。如果这些假设中的任何一个被证明是错误的,则希望OP将来能够学会提出更好的问题。 ;)