正则表达式知识总结

1、前言

正则表达式,如果有一定编程经验的同学,一定知道,可能你知道,但又说不出个所以然来,平时不经常使用,很容易就忘记,有一部分原因,我认为是总结的内容,不够简单和深刻。所以,自己尝试总结一下正则表达式的基础知识点,对于大部分的编程场景应该足够使用啦。另外,也希望通过总结,以便后续忘记时,能快速的记忆起来,这就是本文章的主要内容。

2、正则表达式

RegularExpression.png

匹配字符集

语法 匹配 示例
. 匹配任意字符(除了\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(?=XP|7|8) 能匹配Windows7 中的 “Windows”,但不能匹配 Windows10 中的“Windows”。
(?!exp) 断言自身出现的位置的后面不能匹配表达式 exp,exp1(?!exp2):查找后面不是 exp2 的 exp1。 表达式:Windows(?!XP|7|8) 能匹配Windows10 中的“Windows”,但不能匹配 Windows7 中的“Windows”。
(?<=exp) 断言自身出现的位置的前面能匹配表达式 exp,(?<=exp2)exp1:查找 exp2 后面的 exp1。 表达式:(?<=XP|7|8)Windows 能匹配 7Windows 中的“Windows”,但不能匹配 10Windows 中的“Windows”。
(?<!exp) 断言自身出现的位置的前面不能匹配表达式 exp,(?<!=exp2)exp1:查找前面不是 exp2 的 exp1。 表达式:(?<!XP|7|8)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 。

result = re.match(pattern, string)

等价于

prog = re.compile(pattern)
result = prog.match(string)

注:如果需要多次使用这个正则表达式的话,使用 re.compile() 保存这个正则对象以便复用匹配多个字符串,可以让程序更加高效。

search 函数

re.search 扫描整个字符串并返回第一个成功的匹配。如果没有匹配,就返回一个 None 。

re.search(pattern, string, flags=0)

示例:

>>> re.search('iHTCboy', 'www.iHTCboy.com')
>>> 
<re.Match object; span=(4, 11), match='iHTCboy'>

我们可以使用 group(num)groups() 匹配对象函数来获取匹配表达式。

>>> searchObj = re.search( r'(.*) are (.*?) .*', "iHTCboy are my nikename)
>>> print(searchObj.group())
>>> print(searchObj.groups())
>>> print(searchObj.group(1))
>>> print(searchObj.group(2))
>>> 
iHTCboy are my nikename
('iHTCboy', 'my')
iHTCboy
my

findall 函数

re.findall 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

re.findall(pattern, string, flags=0)

string 返回一个不重复的 pattern 的匹配列表, string 从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。

比如查找字符串中的所有数字:

>>> result2 = re.findall(r'\d+', 'abc123d4efg567')
>>> 
['123', '4', '567']

查找不是以 4、7 结尾的手机号码(11位)手机号码:

>>> result2 = re.findall(r'^1\d{9}[0-35-68-9]$', 'xxxxx')
>>> 

finditer 函数

和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

re.finditer(pattern, string, flags=0)

搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器:

>>> for m in re.finditer(r'\d+', 'abc123d4efg567'):
>>>     print(m.group())
>>>  
123
4
567 

split 函数

re.split 方法按照能够匹配的子串将字符串分割后返回列表。

re.split(pattern, string, maxsplit=0, flags=0)

pattern 分开 string 。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素。

对于规则的字符串,比如 abc 我们一般可以用 split 函数分割,但是如果遇到 a b c 这样的字符串,得到的结果就是 ['a', 'b', '', '', 'c']。所以,你可能需要人工来自己再次过滤,而使用正则表达式来处理,就显示很轻松:

>>> re.split(r'[\s\,]+', 'a,b, c  d')
>>> 
['a', 'b', 'c', 'd']

上面的 [\s\,]+ 表示匹配 \s (空格)和 , 中任意一个至少一次的分割。

对于一个找不到匹配的字符串而言,split 不会对其作出分割。

>>> re.split('a*', 'hello iHTCboy')
>>> 
['hello iHTCboy']

sub 函数

re.sub 用于替换字符串中的匹配项。

re.sub(pattern, repl, string, count=0, flags=0)

返回通过使用 repl 替换在 string 最左边非重叠出现的 pattern 而获得的字符串。 如果样式没有找到,则不加改变地返回 string。 repl 可以是字符串或函数;如为字符串,则其中任何反斜杠转义序列都会被处理。 也就是说,\n 会被转换为一个换行符,\r 会被转换为一个回车附,依此类推。

比如电话号码格式可能带有空格或者-时,可以替换为空字符串:

>>> re.sub(r'\D', "", "188-8888-8888")
>>> 
18888888888

repl 参数可以是一个函数,用于对匹配的内容进行更多的逻辑处理:

# 将匹配的数字乘以 2
>>> def double(matched):
>>>     value = int(matched.group('value'))
>>>     return str(value * 2)
>>>  
>>> re.sub('(?P<value>\d+)', double, 'abc123d4efg567')
>>> 
abc246d8efg1134

总结

正则表达式的内容有非常的多,如果不经常使用的话,可以就会很快忘记。所以,正则其实是不是应该记住呢?其实,应该是要记住,如果记不住,要不要紧?其实,是不要紧,因为正则就想查表一下,你如果之前已经比较系统的学习过,忘记了规则,看一下本文就大概记起来了。所以,这就是本文的目的,总结性的文章,不需要太多的复杂,简单又快捷的总结。

国庆中秋双节快乐!这篇文章应该是去年9月就定下来但迟迟没有动手,现在又过一年了,感叹生活真快。好好学习,天天快乐~

参考


  • 如有疑问,欢迎在评论区一起讨论!
  • 如有不正确的地方,欢迎指导!


注:本文首发于 iHTCboy's blog,如若转载,请注来源


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349