1、前言
正则表达式,如果有一定编程经验的同学,一定知道,可能你知道,但又说不出个所以然来,平时不经常使用,很容易就忘记,有一部分原因,我认为是总结的内容,不够简单和深刻。所以,自己尝试总结一下正则表达式的基础知识点,对于大部分的编程场景应该足够使用啦。另外,也希望通过总结,以便后续忘记时,能快速的记忆起来,这就是本文章的主要内容。
2、正则表达式
匹配字符集
语法 | 匹配 | 示例 |
---|---|---|
. |
匹配任意字符(除了\n ) |
表达式:. 匹配任意一个字符 |
[...] |
匹配字符集(... 表示任意字符) |
表达式:[a-zA-Z] 匹配任意一个小写或大写的字母。表达式: [iHTC] 匹配 “i”, “H”, “T” 和 “C”。表达式: [e-k] 匹配 “e” 到 “k” 之间的字符。 |
\d |
匹配数字 | 表达式:a\d ,匹配字符串 “aaa123” 中的 a1 。表达式: a.\d ,匹配字符串 “aaa123” 中的 aa1 。 |
\D |
匹配非数字 | 表达式:a.\D ,匹配字符串 “aaa123” 中的 aaa 。 |
\s |
匹配空白字符(包括空格、制表符、换页符等空白字符,[ \t\n\r\f] ) |
表达式:a.\s ,匹配字符串 “aaa 123” 中的 aa (最后是空格) 。 |
\S |
匹配非空白字符 | 表达式:a.\S ,匹配字符串 “aaa 123” 中的 aaa 。 |
\w |
匹配单词字符([a-zA-Z0-9_]和汉字) | 表达式:a.\w ,匹配字符串 “aaa 123” 中的 aaa 。 |
\W |
匹配非单词字符 | 表达式:a.\W ,匹配字符串 “aaa 123” 中的 aa (最后是空格) 。 |
\ |
转义字符 | \r , \n 代表回车和换行符; \t 制表符; \\ 代表 “" 本身; \^ 匹配 ^ 符号本身; \$ 匹配 $ 符号本身; \. 匹配小数点(.)本身。 |
匹配量词(Quantifier)
语法 | 匹配 | 示例 |
---|---|---|
* |
匹配表达式出现 0次或者无限次,相当于 {0,} | 表达式:a* ,匹配字符串 “aaa123” 中的 aaa 。 |
+ |
匹配表达式至少出现 1次或者无限次,相当于 {1,} | 表达式:a+ ,匹配字符串 “aaa123” 中的 aaa 。 |
? |
匹配表达式 0次或者 1次,相当于 {0,1} | 表达式:a? ,匹配字符串 “aaa123” 中的 a 。 |
{m,n} |
匹配表达式至少重复 m 次,最多重复 n 次 | 表达式:a{1,2} ,匹配字符串 “aaa123” 中的 aa 。 |
{m} |
匹配表达式重复 m 次 | 表达式:a{3} ,匹配字符串 “aaa123” 中的 aaa 。 |
{m,} |
匹配表达式至少重复 m 次 | 表达式:a{1,} ,匹配字符串 “aaa123” 中的 aaa 。 |
*? |
匹配模式变为非贪婪(尽可能少匹配字符),匹配表达式可以为 0次,也可以是无限次 | 表达式:a[0-9]*?23' ,匹配字符串 “aaa123” 中的 a123 。如果匹配字符串 “aaa23” 则为 a23 。 如果匹配字符串 “aaa456123” 则为 a456123 。 |
+? |
匹配模式变为非贪婪(尽可能少匹配字符),匹配表达式至少 1次,也可以是无限次 | 表达式:a[0-9]+?23 ,匹配字符串 “aaa123” 中的 a123 。如果匹配字符串 “aaa23” 则匹配失败 。 如果匹配字符串 “aaa456123” 则为 a456123 。 |
?? |
匹配模式变为非贪婪(尽可能少匹配字符),匹配表达式最多 1次,也可以是 0次 | 表达式:a[0-9]??23 ,匹配字符串 “aaa123” 中的 a123 。如果匹配字符串 “aaa23” 则为 a23 。 如果匹配字符串 “aaa456123” 则匹配失败。 |
{m,n}? |
非贪婪模式,匹配符合的最短的字符串 | 表达式:a[0-9]{1,4}?23 ,匹配字符串 “aaa123” 中的 a123 。如果匹配字符串 “aaa23” 则匹配失败 。 如果匹配字符串 “aaa456123” 则为 a456123 |
匹配边界
语法 | 匹配 | 示例 |
---|---|---|
^ |
匹配字符串开头 | 表达式:^\d 表示必须以数字开头。表达式: [^abc] 匹配 a , b , c 之外的任意一个字符。 |
$ |
匹配字符串结尾 | 表达式:*.com$ 匹配 .com 结尾的字符串。\d$ 表示必须以数字结束。 |
\A |
指定的字符串匹必须出现在开头 | 表达式:\Aa ,匹配字符串 “aaa123” 中的 a 。 |
\Z |
指定的字符串匹必须出现在结尾。如果是存在换行,只匹配到换行前的结束字符串。 | 表达式:\d\Z ,匹配字符串 “aaa123” 中的 3 。 |
\b |
匹配一个单词边界,但只在单词开始或结尾的位置,即匹配 \w 和 \W 之间。(本身不匹配任何字符) |
表达式:\b4 ,匹配字符串 “aaa123 456” 中的 4 。表达式: \b5 ,匹配字符串 “aaa123 456” 则为失败。 |
\B |
匹配非单词边界,但不能在词的开头或者结尾,即匹配 [^\b] 。(本身不匹配任何字符) |
表达式:\B4 ,匹配字符串 “aaa123 456” 则为失败。表达式: \B5 ,匹配字符串 “aaa123 456” 中的 5 。 |
匹配条件分组
语法 | 匹配 | 示例 |
---|---|---|
⎮ |
或关系,匹配左右任意一个表达式 | 表达式:a⎮b ,匹配字符串 “aaa123” 中的 a 。 |
(ab) |
括号中表达式作为一个分组 | 表达式:(a1) ,匹配字符串 “aaa123” 中的 a1 。表达式: (P⎮p)ython ,可以匹配 Python 或者 python。 |
(?:exp) |
表示非捕获分组,匹配 pattern 但不获取匹配结果。 | 表达式:([a-z]*)([0-9]*)([a-z]*) ,匹配字符串 "aaa123def456" 中的 ‘aaa’, ‘123’, ‘def’ 。表达式: (?:[a-z]*)([0-9]*)([a-z]*) ,匹配字符串 “aaa123def456” 中的 ‘123’, ‘def’ 。 |
\<number> |
引用编号为 number 的分组(规则)匹配到的字符串 | 表达式:(\d)def\1 ,匹配字符串 “aaa123def321” 中的 3def3 。 |
(?P<name>) |
给分组起一个别名 name | 表达式:(?P<tag>\d)def(?P=tag) ,匹配字符串 “aaa123def321” 中的 3def3 。 |
(?P=name) |
引用别名为 name 的分组匹配字符串 | 表达式:<(?P<tag>\w*)>.*</(?P=tag)> ,匹配字符串 <html><h1>www.iHTCboy.com</h1><html> 中的 <h1>www.iHTCboy.com</h1> 。 |
预搜索
语法 | 匹配 | 示例 |
---|---|---|
(?=exp) |
断言自身出现的位置的后面能匹配表达式 exp,exp1(?=exp2):查找 exp2 前面的 exp1。 | 表达式:Windows(?=7) 能匹配Windows7 中的 “Windows”,但不能匹配 Windows10 中的“Windows”。 |
(?!exp) |
断言自身出现的位置的后面不能匹配表达式 exp,exp1(?!exp2):查找后面不是 exp2 的 exp1。 | 表达式:Windows(?!7) 能匹配Windows10 中的“Windows”,但不能匹配 Windows7 中的“Windows”。 |
(?<=exp) |
断言自身出现的位置的前面能匹配表达式 exp,(?<=exp2)exp1:查找 exp2 后面的 exp1。 | 表达式:(?<=7)Windows 能匹配 7Windows 中的“Windows”,但不能匹配 10Windows 中的“Windows”。 |
(?<!exp) |
断言自身出现的位置的前面不能匹配表达式 exp,(?<!=exp2)exp1:查找前面不是 exp2 的 exp1。 | 表达式:(?<!7)Windows 能匹配 10Windows 中的“Windows”,但不能匹配 7Windows 中的“Windows”。 |
exp :expression(表达式)
匹配模式
类型 | 模式 | 说明 |
---|---|---|
IGNORECASE |
忽略大小写模式 | 匹配时忽略大小写。(正则默认是区分大小写的) |
SINGLELINE |
单行模式 | 整个文本看作一个字符串,只有一个开头一个结尾 |
MULTILINE |
多行模式 | 每行都是一个字符串。在多行模式下,如果需要仅匹配字符串开始和结束位置,可以使用 \A 和 \Z |
3、Python 正则表达式
如果使用编辑语言来执行正则表达式呢?所有语言都是相通的,这是记录了之前使用 Python 的 re 模块做正则匹配的功能。
compile 和 match 函数
re.compile
函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match()、search() 等函数使用。re.match
尝试从字符串的起始位置匹配一个模式,如果字符串开始不符合正则表达式,就返回 None 。
1 | result = re.match(pattern, string) |
等价于
1 | prog = re.compile(pattern) |
注:如果需要多次使用这个正则表达式的话,使用
re.compile()
保存这个正则对象以便复用匹配多个字符串,可以让程序更加高效。
search 函数
re.search
扫描整个字符串并返回第一个成功的匹配。如果没有匹配,就返回一个 None 。
1 | re.search(pattern, string, flags=0) |
示例:
1 | >>> re.search('iHTCboy', 'www.iHTCboy.com') |
我们可以使用 group(num)
或 groups()
匹配对象函数来获取匹配表达式。
1 | >>> searchObj = re.search( r'(.*) are (.*?) .*', "iHTCboy are my nikename) |
findall 函数
re.findall
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
1 | re.findall(pattern, string, flags=0) |
对
string
返回一个不重复的pattern
的匹配列表,string
从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。
比如查找字符串中的所有数字:
1 | >>> result2 = re.findall(r'\d+', 'abc123d4efg567') |
查找不是以 4、7 结尾的手机号码(11位)手机号码:
1 | >>> result2 = re.findall(r'^1\d{9}[0-35-68-9]$', 'xxxxx') |
finditer 函数
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
1 | re.finditer(pattern, string, flags=0) |
搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器:
1 | for m in re.finditer(r'\d+', 'abc123d4efg567'): |
split 函数
re.split
方法按照能够匹配的子串将字符串分割后返回列表。
1 | re.split(pattern, string, maxsplit=0, flags=0) |
用
pattern
分开string
。如果maxsplit
非零, 最多进行maxsplit
次分隔, 剩下的字符全部返回到列表的最后一个元素。
对于规则的字符串,比如 abc
我们一般可以用 split 函数分割,但是如果遇到 a b c
这样的字符串,得到的结果就是 ['a', 'b', '', '', 'c']
。所以,你可能需要人工来自己再次过滤,而使用正则表达式来处理,就显示很轻松:
1 | >>> re.split(r'[\s\,]+', 'a,b, c d') |
上面的 [\s\,]+
表示匹配 \s
(空格)和 ,
中任意一个至少一次的分割。
对于一个找不到匹配的字符串而言,split 不会对其作出分割。
1 | >>> re.split('a*', 'hello iHTCboy') |
sub 函数
re.sub
用于替换字符串中的匹配项。
1 | re.sub(pattern, repl, string, count=0, flags=0) |
返回通过使用
repl
替换在string
最左边非重叠出现的pattern
而获得的字符串。 如果样式没有找到,则不加改变地返回 string。repl
可以是字符串或函数;如为字符串,则其中任何反斜杠转义序列都会被处理。 也就是说,\n
会被转换为一个换行符,\r
会被转换为一个回车附,依此类推。
比如电话号码格式可能带有空格或者-时,可以替换为空字符串:
1 | >>> re.sub(r'\D', "", "188-8888-8888") |
repl
参数可以是一个函数,用于对匹配的内容进行更多的逻辑处理:
1 | # 将匹配的数字乘以 2 |
总结
正则表达式的内容有非常的多,如果不经常使用的话,可以就会很快忘记。所以,正则其实是不是应该记住呢?其实,应该是要记住,如果记不住,要不要紧?其实,是不要紧,因为正则就想查表一下,你如果之前已经比较系统的学习过,忘记了规则,看一下本文就大概记起来了。所以,这就是本文的目的,总结性的文章,不需要太多的复杂,简单又快捷的总结。
国庆中秋双节快乐!这篇文章应该是去年9月就定下来但迟迟没有动手,现在又过一年了,感叹生活真快。好好学习,天天快乐~
参考
- 如有疑问,欢迎在评论区一起讨论!
- 如有不正确的地方,欢迎指导!
注:本文首发于 iHTCboy’s blog,如若转载,请注来源