6. 字符串忽略大小写的搜索替换
>>> text = 'UPPER PYTHON, lower python, Mixed Python'
>>> re.findall('python', text, flags=re.IGNORECASE)
['PYTHON', 'python', 'Python']
>>> re.sub('python', 'snake', text, flags=re.IGNORECASE)
'UPPER snake, lower snake, Mixed snake'
>>>
7.最短匹配(?)
Q: 你正在试着用正则表达式匹配某个文本模式,但是它找到的是模式的最长可能匹配。 而你想修改它变成查找最短的可能匹配。
S:这个问题一般出现在需要匹配一对分隔符之间的文本的时候(比如引号包含的字符串)。 为了说明清楚,考虑如下的例子:模式 r'\"(.*)\"'
的意图是匹配被双引号包含的文本。(.)匹配除了换行外的任何字符
>>> str_pat = re.compile(r'"(.*)"')
>>> text1 = 'Computer says "no."'
>>> str_pat.findall(text1)
['no.']
>>> text2 = 'Computer says "no." Phone says "yes."'
>>> str_pat.findall(text2)
['no." Phone says "yes.']
第二个例子中搜索 text2
的时候返回结果并不是我们想要的。
为了修正这个问题,可以在模式中的*操作符后面加上?修饰符,就像这样:
>>> str_pat = re.compile(r'"(.*?)"')
>>> str_pat.findall(text2)
['no.', 'yes.']
>>>
通过在 *
或者 +
这样的操作符后面添加一个 ?
可以强制匹配算法改成寻找最短的可能匹配。
8. 多行匹配模式
S:这个问题很典型的出现在当你用点(.)去匹配任意字符的时候,忘记了点(.)不能匹配换行符的事实。 比如,假设你想试着去匹配C语言分割的注释:
>>> comment = re.compile(r'/\*(.*?)\*/')
>>> text1 = '/* this is a comment */'
>>> text2 = '''/* this is a
... multiline comment */
... '''
>>>
>>> comment.findall(text1)
[' this is a comment ']
>>> comment.findall(text2)
[]
>>>
为了修正这个问题,你可以修改模式字符串,增加对换行的支持。比如:
#(?:.|\n)
指定了一个非捕获组 (也就是它定义了一个仅仅用来做匹配,而不能通过单独捕获或者编号的组)
>>> comment = re.compile(r'/\*((?:.|\n)*?)\*/')
>>> comment.findall(text2)
[' this is a\n multiline comment ']
>>>
(?:x)
匹配 'x' 但是不记住匹配项。这种叫作非捕获括号,使得你能够定义为与正则表达式运算符一起使用的子表达式。来看示例表达式 /(?:foo){1,2}/。如果表达式是 /foo{1,2}/,{1,2}将只对 ‘foo’ 的最后一个字符 ’o‘ 生效。如果使用非捕获括号,则{1,2}会匹配整个 ‘foo’ 单词。
re.compile()
函数接受一个标志参数叫 re.DOTALL
,在这里非常有用。 它可以让正则表达式中的点(.)匹配包括换行符在内的任意字符。比如:
>>> comment = re.compile(r'/\*(.*?)\*/', re.DOTALL)
>>> comment.findall(text2)
[' this is a\n multiline comment ']
9. 将Unicode文本标准化
>>> s1 = 'Spicy Jalape\u00f1o'
>>> s2 = 'Spicy Jalapen\u0303o'
>>> s1
'Spicy Jalapeño'
>>> s2
'Spicy Jalapeño'
>>> s1 == s2
False
>>> len(s1)
14
>>> len(s2)
15 import unicodedata
>>> t1 = unicodedata.normalize('NFC', s1)
>>> t2 = unicodedata.normalize('NFC', s2)
>>> t1 == t2
True
>>> print(ascii(t1))
'Spicy Jalape\xf1o'
>>> t3 = unicodedata.normalize('NFD', s1)
>>> t4 = unicodedata.normalize('NFD', s2)
>>> t3 == t4
True
>>> print(ascii(t3))
'Spicy Jalapen\u0303o'
>>>
NFC表示字符应该是整体组成(比如可能的话就使用单一编码),而NFD表示字符应该分解为多个组合字符表示。Python同样支持扩展的标准化形式NFKC和NFKD
10. 在正则式中使用Unicode
re
模块已经对一些Unicode字符类有了基本的支持。 比如, \\d
已经匹配任意的unicode数字字符了