使用Python re模块匹配正则表达式组(带或)和特殊字符

时间:2013-11-24 13:22:44

标签: python regex

我正在尝试拆分字符串,如下面的标题,并匹配名称,然后匹配任何其他信息,在括号/大括号中或破折号后(常规,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'}
{}
{}
{}

3 个答案:

答案 0 :(得分:2)

使用unicode文字。否则,[-–—―]匹配-\xe2\x80\x93\xe2\x80\x94\xe2\x80\x95代替-

# -*- coding: utf-8 -*-
import re
titles = [
    u'Spaced (News)',
    u'Angry Birds [Game]',
    u'Cheats - for all games', # dash
    u'Cheats – for all games', # ndash
    u'Cheats — for all games', # mdash
    u'Cheats ― for all games'  # horizontal bar
]
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'}

>>> r'[–]'
'[\xe2\x80\x93]'
>>> re.findall(r'[–]', '–')
['\xe2', '\x80', '\x93']
>>> re.findall(ur'[–]', u'–')
[u'\u2013']
>>> print re.findall(ur'[–]', u'–')[0]
–

答案 1 :(得分:2)

Unicode有“字符”或“符号”占用多个字节,Python不太擅长理解这个概念,因此有时会有一些小问题。您可以执行以下操作之一:

您可以尝试确保您要解析的所有字符串都是unicode,如果您控制这些字符串应该很简单 - 例如,只需将u指示符添加到字符串的开头即可像这样:

u'Spaced (News)',
u'Angry Birds [Game]',
u'Cheats - for all games', # dash
u'Cheats – for all games', # ndash
u'Cheats — for all games', # mdash
u'Cheats ― for all games'  # horizontal bar

并将其添加到正则表达式中,如下所示:

ur'^(?P<name>.+)\s+(([-–—―]\s+(?P<addition_a>.+))|([\(\[](?P<addition_b>.+)[\)\]]))$'

否则,或者如果你无法控制,你可以做一个小的修改 - 虽然不完全正确 - 会起作用。该更改是接受来自[-–—―]集的多个字符,而不只是通过[-–—―]+接受单个字符:

r'^(?P<name>.+)\s+(([-–—―]+\s+(?P<addition_a>.+))|([\(\[](?P<addition_b>.+)[\)\]]))$'

这些选项中的任何一个都会产生你想要的东西。

第一个将导致unicode结果:

>>> 
{'addition': u'News', 'name': u'Spaced'}
{'addition': u'Game', 'name': u'Angry Birds'}
{'addition': None, 'name': u'Cheats'}
{'addition': None, 'name': u'Cheats'}
{'addition': None, 'name': u'Cheats'}
{'addition': None, 'name': u'Cheats'}

常规字符串中的第二个:

>>> 
{'addition': 'News', 'name': 'Spaced'}
{'addition': 'Game', 'name': 'Angry Birds'}
{'addition': None, 'name': 'Cheats'}
{'addition': None, 'name': 'Cheats'}
{'addition': None, 'name': 'Cheats'}
{'addition': None, 'name': 'Cheats'}

答案 2 :(得分:2)

稍微多一点“雪橇”的方式是将整个重新改为“一些词语和空格,直到它不是,然后是其余的”。这也避免了可选的additional_aadditional_b命名组以及try / except逻辑。

示例:

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'}