注意:把必须匹配的情况考虑周全并写出一个匹配结果符合预期的正则表达式很容易,但把不需要匹配的情况也考虑周全并确保它们都被排除在匹配结果之外往往要困难得多。
一、常用字符含义
1.常用的字符含义
字符 | 描述 | 表达式 | 可能匹配结果 |
---|---|---|---|
普通字符 | 匹配自身 | abc | abc |
. | 匹配任意除换行符"\n"外的单个字符(在DOTALL模式中也能匹配换行符) | a.c | abc |
\ | 转义字符,使后一个字符改变原来的意思
| a\.c a\\c .a.\. | a.c a\c na1.xls |
* | 匹配前一个字符0或多次 | abc* | ab abccc |
+ | 匹配前一个字符1次或多次 | abc+ | abc abccc |
? | 匹配一个字符0次或1次 | abc? | ab abc |
^ | 匹配字符串开头。在多行模式中匹配每一行的开头 | ^abc | abc |
$ | 匹配字符串末尾,在多行模式中匹配每一行的末尾 | abc$ | abc |
| | 或。匹配|左右表达式任意一个,从左到右匹配,如果|没有包括在()中,则它的范围是整个正则表达式 | abc|def | abc def |
{ } | {m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,{m,}至少m次 | ab{1,2}c | abc abbc |
[ ] | 字符集。对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,如[abc]或[a-c]。[^abc]表示取反,即非abc。 所有特殊字符在字符集中都失去其原有的特殊含义。用\反斜杠转义恢复特殊字符的特殊含义。 | a[bcd]e | abe ace ade |
() | 被括起来的表达式将作为分组,从表达式左边开始每遇到一个分组的左括号“(”,编号+1. 分组表达式作为一个整体,可以后接数量词。表达式中的|仅在该组中有效。 | (abc){2}a(123|456)c | abcabc a456c |
2.预定义字符集
字符 | 描述 | 表达式 | 可能匹配结果 |
---|---|---|---|
\S | 非空白字符:[^\s] | a\Sc | abc |
\D | 非数字:[^\d] | a\Dc | abc |
\d | 数字:[0-9] | a\bc | a1c |
\W | 匹配非字母字符,即匹配特殊字符 | a\Wc | a c |
\w | 匹配包括下划线在内的任何字字符:[A-Za-z0-9_],并不完全等同[A-Za-z0-9_],\w包含俄文,希腊文等 | a\wc | abc |
\s | 匹配任何空白字符:[<空格>\t\r\n\f\v] | a\sc | a c |
\b | 匹配\w和\W之间,即匹配单词边界匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 | \babc\b | 空格abc空格 |
\Z | 仅匹配字符串结尾,同$ | abc\Z | abc |
\A | 仅匹配字符串开头,同^ | \Aabc | abc |
\B | [^\b],不是单词边界 | a\Bbc | abc |
3.空白元字符
元字符 | 说明 |
[\b] | 回退并删除一个字符(Backspace键) |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\t | 制表符(Tab键) |
\v | 垂直制表符 |
4.回溯引用匹配
\1表示模式中的第一个子表达式,\2表示模式中的第二个表达式
注意:
- 不同语言在实现回溯引用的语法往往有巨大差异
- 回溯引用只能用来引用模式里的子表达式,即用()括起来的片段
- 回溯引用通常从1开始计数
5.向前向后查找
例如:(?<=<h1>).+?(?=<\/h1>)
?<=向前匹配符,字符串必须以<h1>开头;?=为向后匹配符,所字符串必须以</h1>为结尾;.+?表示最小匹配
二、正则表达式优化
1.使用字符组代替分支条件
使用[a-d]表示a~d之间的字母,而不是使用(a|b|c|d)
2.优化选择最左端的匹配结果
对于传统NFA引擎来说,因为引擎一旦找到匹配结果就会停下来,而不会去尝试正则表达式的每一种可能
3.谨慎用点号元字符,尽可能不用星号和加号这样的任意量词
4.尽量使用字符串函数处理代替正则表达式
使用字符串函数和正则表达式都可以处理字符串,两者相比,字符串函数处理的效率更高。当然,有些情况几乎是非正则表达式不能胜任的,或者不用正则表达式的成本太高,这些情况不得不用正则表达式。
5.合理使用括号
每使用一个普通括号(),而不是非捕获型括号(?:),就会保留一部分内存等着再次访问。
6.起始、行描点优化
能确定起止位置,使用^能提高匹配的速度。同理,使用$标记结尾,正则引擎则会从符合条件的长度处开始匹配,略过目标字符串中许多可能的字符。在写正则表达式时,应该将描点独立出来,例如“^(?:abc|123)”比“^123|^abc”效率高,而“^(abc)”比“(^abc)”效率更高。
三、python中的正则表达式
1.re.match():尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
语法:re.match(pattern, string, flags=0)
参数 | 描述 |
---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
匹配成功re.match方法返回一个匹配的对象,否则返回None。
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'(.*) are (.*?) .*', line, re.M | re.I)
if matchObj:
print("matchObj.group() : ", matchObj.group())
print("matchObj.group(1) : ", matchObj.group(1))
print("matchObj.group(2) : ", matchObj.group(2))
else:
print("No match!!")
2.re.search方法:扫描整个字符串并返回第一个成功的匹配。
语法:re.search(pattern, string, flags=0)
import re
line = "Cats are smarter than dogs"
matchObj = re.search(r'(.*) are (.*?) .*', line, re.M | re.I)
if matchObj:
print("matchObj.group() : ", matchObj.group())
print("matchObj.group(1) : ", matchObj.group(1))
print("matchObj.group(2) : ", matchObj.group(2))
else:
print("No match!!")
re.match与re.search的区别:re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'dogs', line, re.M | re.I)
matchObj2 = re.search(r'dogs', line, re.M | re.I)
if matchObj:
print("matchObj.group() : ", matchObj.group())
else:
print("matchObj no match!!")
if matchObj2:
print("matchObj2.group() : ", matchObj2.group())
else:
print("matchObj2 no match!!")
3.re.sub方法:用于替换字符串中的匹配项
语法:re.sub(pattern, repl, string, count=0, flags=0)
- pattern : 正则中的模式字符串。
- repl : 替换的字符串,也可为一个函数。
- string : 要被查找替换的原始字符串。
- count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
import re
phone = "2004-959-559 # 这是一个国外电话号码"
# 删除字符串中的 Python注释
num = re.sub(r'#.*$', "", phone)
print("电话号码是: ", num)
# 删除非数字(-)的字符串
num = re.sub(r'\D', "", phone)
print("电话号码是: ", num)
4.re.compile方法:用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
语法:re.compile(pattern[, flags])
- pattern : 一个字符串形式的正则表达式
- flags : 可选,表示匹配模式,比如忽略大小写,多行模式
import re
s = 'one12twothree34four'
pattern = re.compile(r'\d+')
m = pattern.match(s) # 查找头部,没有匹配
print(m)
m = pattern.match('one12twothree34four', 3, 10).span()
print(m)
5.re.findall方法:在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
语法:findall(string[, pos[, endpos]])
- string : 待匹配的字符串。
- pos : 可选参数,指定字符串的起始位置,默认为 0。
- endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。
import re
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('runoob 123 google 456')
result2 = pattern.findall('run88oob123google456', 0, 10)
print(result1)
print(result2)
6.re.finditer方法: 和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
import re
it = re.finditer(r"\d+", "12a32bc43jf3")
for match in it:
print(match.group())
7.re.split方法:按照能够匹配的子串将字符串分割后返回列表
语法:re.split(pattern, string[, maxsplit=0, flags=0])
import re
m = re.split('\W+', 'runoob, runoob, runoob.')
print(m)