在使用 Python 的 re 模块匹配正则表达式组时,遇到了一些问题。具体如下:
- 我想匹配类似下面的标题,提取名称和附加信息。其中,附加信息可能包含在括号/大括号中,也可能紧跟在名称之后,并用各种形式的破折号(短横线、m/n 破折号、水平线)分隔。
- 正则表达式似乎是正确的,在其他正则表达式测试工具上也能正常工作,但在 Python 中运行时却遇到了问题。
- 有些奇怪的事情发生了。第一个带有破折号的标题似乎匹配上了,但 addition_a 组没有包含正确的字符串。此外,各种破折号等特殊字符都没有匹配。脚本的编码是 utf-8,所以我假设原始正则表达式字符串中的破折号应该可以正常工作,但事实并非如此。
以下是代码示例:
# -*- coding: utf-8 -*-
import re
titles = [
'Spaced (News)',
'Angry Birds [Game]',
'Cheats - for all games', # dash
'Cheats – for all games', # ndash
'Cheats — for all games', # mdash
'Cheats ― for all games' # horizontal bar
]
regex = re.compile(r'^(?P<name>.+)\s+(([-–—―]\s+(?P<addition_a>.+))|([\(\[](?P<addition_b>.+)[\)\]]))$')
for title in titles:
data = {
}
match = regex.match(title.strip())
if match:
data['name'] = match.group('name')
try:
data['addition'] = match.group('addition_a')
except IndexError:
pass
try:
data['addition'] = match.group('addition_b')
except IndexError:
pass
print(data)
脚本输出如下:
{'addition': 'News', 'name': 'Spaces'}
{'addition': 'Game', 'name': 'Angry Birds'}
{'addition': None, 'name': 'Cheats'}
{}
{}
{}
2、解决方案:
找到了以下三种解决该问题的方案:
方法一:使用 unicode 文字量(Unicode Literals)
unicode 文字量能够确保字符串中的字符按照 UTF-8 编码正确存储,从而避免了 Python 在处理某些特殊字符时可能出现的混淆。
# -*- coding: utf-8 -*-
import re
titles = [
u'Spaced (News)',
u'Angry Birds [Game]',
u'Cheats - for all games',
u'Cheats – for all games',
u'Cheats — for all games',
u'Cheats ― for all games',
]
regex = re.compile(ur'^(?P<name>.+)\s+(([-–—―]\s+(?P<addition_a>.+))|([\(\[](?P<addition_b>.+)[\)\]]))$')
for title in titles:
match = regex.match(title.strip())
if match:
data = {
}
data['name'] = match.group('name')
data['addition'] = match.group('addition_a') or match.group('addition_b')
print(data)
脚本的输出结果为:
{'addition': u'News', 'name': u'Spaced'}
{'addition': u'Game', 'name': u'Angry Birds'}
{'addition': u'for all games', 'name': u'Cheats'}
{'addition': u'for all games', 'name': u'Cheats'}
{'addition': u'for all games', 'name': u'Cheats'}
{'addition': u'for all games', 'name': u'Cheats'}
方法二:将特殊字符集合扩展为 [-―](不建议使用)
Python 难以识别在非 ASCII 字符集中的字符。因此,需要将正则表达式中的特殊字符集合从 [-–—―] 扩展到 [-―],以确保能够匹配到所有类型的破折号。
# -*- coding: utf-8 -*-
import re
titles = [
'Spaced (News)',
'Angry Birds [Game]',
'Cheats - for all games', # dash
'Cheats – for all games', # ndash
'Cheats — for all games', # mdash
'Cheats ― for all games' # horizontal bar
]
regex = re.compile(r'^(?P<name>.+)\s+(([-_-_―]+\s+(?P<addition_a>.+))|([\(\[](?P<addition_b>.+)[\)\]]))$')
for title in titles:
match = regex.match(title.strip())
if match:
data = {
}
data['name'] = match.group('name')
data['addition'] = match.group('addition_a') or match.group('addition_b')
print(data)
脚本的输出结果为:
{'addition': 'News', 'name': 'Spaced'}
{'addition': 'Game', 'name': 'Angry Birds'}
{'addition': 'for all games', 'name': 'Cheats'}
{'addition': 'for all games', 'name': 'Cheats'}
{'addition': 'for all games', 'name': 'Cheats'}
{'addition': 'for all games', 'name': 'Cheats'}
方法三:使用更简单的正则表达式匹配标题
可以使用更简单的正则表达式,将标题分成单词和空格,然后根据空格和各种破折号将它们拆分成名称和附加信息。
for title in titles:
data = dict(zip(['name', 'addition'], (m.strip() for m in re.findall('([\w\s]+)', title))))
print(data)
脚本的输出结果为:
{'addition': 'News', 'name': 'Spaced'}
{'addition': 'Game', 'name': 'Angry Birds'}
{'addition': 'for all games', 'name': 'Cheats'}
{'addition': 'for all games', 'name': 'Cheats'}
{'addition': 'for all games', 'name': 'Cheats'}
{'addition': 'for all games', 'name': 'Cheats'}
请根据您的实际情况选择合适的方法来解决正则表达式匹配特殊字符的问题。