基于上下文的字符串分割在Python中
抱歉如果这有点重复,但我在网上搜索了很久,没找到相关的信息。
我有一个来自化学数据库的字符串,其中的分隔符(逗号)有时会出现在我想要拆分的项目中。比如说,有这样一个字符串:
s = '2-Methyl-3-phythyl-1,4-naphthochinon,Vitamin, K1,Antihemorrhagic vitamin'
在这个例子中,正确的拆分结果应该是:
splitS = ['2-Methyl-3-phythyl-1,4-naphthochinon', 'Vitamin, K1', 'Antihemorrhagic vitamin']
我认为,最准确的拆分方法是只在那些旁边没有空格的逗号上进行拆分,并且这些逗号周围不能有两个数字。这样的话,像'1,4'和'Vitamin, K1'这样的情况就不会被拆分,而是能正确拆分出三个化学名称。
我尝试过使用正则表达式,但没有成功。我可以分享一些我尝试过的内容,但基本上没什么用。非常感谢大家的帮助。
编辑:我本该一开始就提到这一点。通过一些尝试,以及@Borealid提供的更优雅的解决方案,我已经正确找到了拆分的位置,但输出结果却很糟糕,比如:
>>> s = '2-Methyl-3-phythyl-1,4-naphthochinon,Vitamin, K1,Antihemorrhagic vitamin'
>>> pat = re.compile("([^\d\s],[^\d\s])|([^\s],[^\d\s])|([^\d\s],[^\s])")
>>> re.split(pat, s)
['2-Methyl-3-phythyl-1,4-naphthochino', 'n,V', None, None, 'itamin, K', None, '1,A', None, 'ntihemorrhagic vitamin']
看起来应该有一种方法,先找到正确的逗号进行拆分,然后只在这些逗号上拆分,这样就能避免名称被破坏。
再次感谢大家!
3 个回答
类似于 ([^\d\s],[^\d\s])|([^\s],[^\d\s])|([^\d\s],[^\s])
吗?
这个意思是:逗号的两边要么没有数字,要么一边有数字而另一边没有,或者一边有数字而另一边是空白。
在所有情况下,逗号旁边不能有空格。
\d
代表“数字”。\s
代表“空白字符”。[]
是一个字符类,而 [^]
是一个反向字符类(意思是“匹配不在后面内容中的字符”)。
这个规则不会在字符串的开头或结尾的逗号上进行分割,但我觉得这应该不是个大问题。
我有一个解决方案,不过有点长。好了,开始吧:
s = '2-Methyl-3-phythyl-1,4-naphthochinon,Vitamin, K1,Antihemorrhagic vitamin'
首先,我们要找到字符串中所有逗号的位置,这些位置会存放在 all_commas
里;然后找到所有特殊逗号的位置,这些会放在 special_commas
里:
all_commas = [match.start() for match in re.finditer(r',', s)]
special_commas = [match.start()+1 for match in re.finditer(r'\d,\d|.,\s', s)]
接下来,我们计算这些位置之间的差值,这些差值会存放在 split_commas
里。现在,我们就得到了要分割的位置:
split_commas = set(all_commas) - set(special_commas)
然后,我们会遍历这些分割位置,把分割出来的字符串保存在 splitS
里:
splitS = []
start = -1
for end in sorted(split_commas) + [None]:
splitS.append(s[start+1:end])
start = end
最后,这就是我们在 splitS
中得到的结果:
>>> splitS
['2-Methyl-3-phythyl-1,4-naphthochinon', 'Vitamin, K1', 'Antihemorrhagic vitamin']
你可以通过使用前后查找的方式,来只匹配符合你描述的逗号:
(?<!\d),(?! )|(?<=\d),(?![\d ])
而且它似乎在你的示例字符串中表现得很正确:
>>> re.split(r'(?<!\d),(?! )|(?<=\d),(?![\d ])', s)
['2-Methyl-3-phythyl-1,4-naphthochinon', 'Vitamin, K1', 'Antihemorrhagic vitamin']
下面是解释:
(?<!\d), # match a comma that is not preceeded by a digit...
(?! ) # ... as long as it is not followed by a space
| # OR
(?<=\d), # match a comma that is preceeded by a digit...
(?![\d ]) # ... as long as it is not followed by a digit or a space
在写完解释后,我意识到正则表达式中的(?<=\d)
部分其实是多余的,因为它在正则表达式的第一部分不匹配时已经隐含了这个意思。这意味着你可以把它简化成下面的形式,依然能得到相同的效果:
(?<!\d),(?! )|,(?![\d ])