
|----------------|
| 你好,我是安然无虞。 |
文章目录

正则表达式
正则表达式是一种高级文本处理技术, 通过特征匹配、特征搜索和特征替换功能完成对复杂字符串的校验、字符串搜索、和替换操作.
正则表达式是一些由字符和特殊符号组成的字符串, 这些字符和特殊符号有特殊意义, 用来描述一个字符串的组成方式.
正则表达式语法
下面是正则表达式中常见的语法, 这里先了解, 后面会详细一一介绍.
| 表达式 | 描述 |
|---|---|
| \d | 数字 |
| \D | 非数字 |
| \w | 字母数字下划线 |
| \s | 所有空白符,包括换行符 |
| \S | 非空白符,不包括换行符 |
| . | 任意字符, 不包括换行符 |
| [] | 枚举, 匹配括号里出现的字符 |
| [^] | 枚举取反, 匹配中括号里出现的字符之外的字符 |
| * | 子表达式零次或多次 |
| + | 子表达式一次或多次 |
| ? | 子表达式零次或一次,或指明一个非贪婪限定符 |
| \ | 转义符 |
| ^ | 匹配字符串的开头 |
| $ | 匹配字符串的结尾 |
| {n,m}, {m} | 匹配前面的子表达式至少n次,至多m次; 匹配前面的子表达式m次 |
| | | 或 |
| () | 分组 |
| (?: ) | 非捕获组 |
| exp1(?=exp2) | 匹配exp2 前面的 exp1 |
| (?<=exp2)exp1 | 匹配 exp2 前面的 exp1 |
| exp1(?!exp2) | 匹配后面不是 exp2 的 exp1 |
| (?<!exp2)exp1 | 匹配前面不是 exp2 的 exp1 |
贪婪和非贪婪
贪婪时候的运行情况:
python
import re
# 贪婪
string = '打lol游戏, 打csgo游戏, 打格斗游戏, 打好玩的游戏'
result = re.search(r'打.*游戏', string)
print(result.group())
# 运行结果:
打lol游戏, 打csgo游戏, 打格斗游戏, 打好玩的游戏
非贪婪时候的运行情况:
python
import re
# 非贪婪
string = '打lol游戏, 打csgo游戏, 打格斗游戏, 打好玩的游戏'
# 当?出现在形容数量的字符前, 代表该子表达式为 非贪婪.
# 没有❓就是贪婪.
# 形容数量的字符: .* .+ .{m,n} ?
result = re.search(r'打.*?游戏', string)
print(result.group())
# 运行结果:
打lol游戏
+ 和 * 的区别
*: 匹配子表达式零次或多次+: 匹配子表达式一次或多次- \s: 所有空白符, 包括换行符
python
text = '正则表达式 很好用'
text2 = '正则表达式很好用'
for t in [text, text2]:
regex = re.search(r'正则表达式\s*很好用', t)
if regex:
print(regex.group())
else:
print('未获取到')
运行结果是:
text
正则表达式 很好用
正则表达式很好用
上面的代码如果使用的是 + , 则不会查找到 text2 文件.
re.search()
python
import re
from re import I, M, S, X, DOTALL, L, U
"""
I: 忽略大小写.
M: 拆分行, 多行匹配. 这个是影响^$.
S: 忽略换行符. 也可以理解为使"."匹配所有字符.
X: 添加注释, 使正则表达式便于理解.
DOTALL: 等价于S.
U: python3不用了.
"""
text = '正则表达式是一种高级文本处理\n技术,通过特征匹配、特征搜索和特征替换功能完成对复杂字符串的\n校验、字符串搜索、和替换操作'
result = re.search(pattern=r'.*', string=text, flags=re.S)
print(result.group())
运行结果是:
text
正则表达式是一种高级文本处理
技术,通过特征匹配、特征搜索和特征替换功能完成对复杂字符串的
校验、字符串搜索、和替换操作
python
import re
from re import I, M, S, X, DOTALL, L, U
"""
I: 忽略大小写.
M: 拆分行, 多行匹配. 这个是影响^$.
S: 忽略换行符. 也可以理解为使"."匹配所有字符.
X: 添加注释, 使正则表达式便于理解.
DOTALL: 等价于S.
U: python3不用了.
"""
text = '正则表达式是一种高级文本处理\n技术,通过特征匹配、特征搜索和特征替换功能完成对复杂字符串的\n校验、字符串搜索、和替换操作'
result = re.findall(r'^.*$', text, re.M)
print(result)
result = re.findall(r'.*', text, re.S)
print(result)
运行结果是:
text
['正则表达式是一种高级文本处理', '技术,通过特征匹配、特征搜索和特征替换功能完成对复杂字符串的', '校验、字符串搜索、和替换操作']
['正则表达式是一种高级文本处理\n技术,通过特征匹配、特征搜索和特征替换功能完成对复杂字符串的\n校验、字符串搜索、和替换操作', '']
好的, 注意理解上面运行结果的区别.
re.split()
re.split()方法的作用类似于Python中的split(), 但是功能比Python中的多.
python
import re
# 需求: 根据逗号拆分为有多个元素的列表.
text = '安然, 老板, 芜湖, 周杰伦'
print(text.split(','))
# # 需求: 根据特殊符号拆分有多个元素的列表.
text2 = '安然, 老板; 芜湖. 周杰伦'
regex = re.split(r'[,;.]', text2)
print(regex)
运行结果是:
text
['安然', ' 老板', ' 芜湖', ' 周杰伦']
['安然', ' 老板', ' 芜湖', ' 周杰伦']
匹配和替换
python
import re
text = '11122233303334455566789K'
regex = re.compile(r'\d*')
# match实际上等价于search的表达式前面加上^(从开头匹配)
result = regex.match(text)
print(result.group())
# fullmatch实际上等价于search的表达式前面加上^, 结尾加上$(匹配开头结尾)
result2 = regex.fullmatch(text)
print(result2.group())
result3 = re.search(r'^\d*$', text)
if result3:
print(1111)
result4 = regex.sub('a', text)
print(result4)
运行结果是:
text
11122233303334455566789
报错: AttributeError: 'NoneType' object has no attribute 'group'
没有打印1111
aaKa
re.findall() 和 re.finditer()
在当前目录下有个kyj.txt文件, 里面有这么一篇关于孔乙己的文章:
text
鲁镇的酒店的格局,是和别处不同的:都是当街一个曲尺形的大柜台,柜里面预备着热水,可以随时温酒。做工的人,傍午傍晚散了工,每每花四文铜钱,买一碗酒,------这是二十多年前的事,现在每碗要涨到十文,------靠柜外站着,热热的喝了休息;倘肯多花一文,便可以买一碟盐煮笋,或者茴香豆,做下酒物了,如果出到十几文,那就能买一样荤菜,但这些顾客,多是短衣帮⑴,大抵没有这样阔绰⑵。只有穿长衫的,才踱进店面隔壁的房子里,要酒要菜,慢慢地坐喝。
我从十二岁起,便在镇口的咸亨酒店里当伙计,掌柜说,样子太傻,怕侍候不了长衫主顾,就在外面做点事罢。外面的短衣主顾,虽然容易说话,但唠唠叨叨缠夹不清的也很不少。他们往往要亲眼看着黄酒从坛子里舀出,看过壶子底里有水没有,又亲看将壶子放在热水里,然后放心:在这严重监督下,羼⑶水也很为难。所以过了几天,掌柜又说我干不了这事。幸亏荐头⑷的情面大,辞退不得,便改为专管温酒的一种无聊职务了。
我从此便整天的站在柜台里,专管我的职务。虽然没有什么失职,但总觉得有些单调,有些无聊。掌柜是一副凶脸孔,主顾也没有好声气⑸,教人活泼不得;只有孔乙己到店,才可以笑几声,所以至今还记得。
孔乙己是站着喝酒而穿长衫的唯一的人。他身材很高大;青白脸色,皱纹间时常夹些伤痕;一部乱蓬蓬的花白的胡子。穿的虽然是长衫,可是又脏又破,似乎十多年没有补,也没有洗。他对人说话,总是满口之乎者也⑹,教人半懂不懂的。因为他姓孔,别人便从描红纸上的"上大人孔乙己⑺"这半懂不懂的话里,替他取下一个绰号,叫作孔乙己。孔乙己一到店,所有喝酒的人便都看着他笑,有的叫道,"孔乙己,你脸上又添上新伤疤了!"他不回答,对柜里说,"温两碗酒,要一碟茴香豆。"便排出九文大钱。他们又故意的高声嚷道,"你一定又偷了人家的东西了!"孔乙己睁大眼睛说,"你怎么这样凭空污人清白......""什么清白?我前天亲眼见你偷了何家的书,吊着打。"孔乙己便涨红了脸,额上的青筋条条绽出,争辩道,"窃书不能算偷......窃书!......读书人的事,能算偷么?"接连便是难懂的话,什么"君子固穷⑻",什么"者乎"之类,引得众人都哄笑起来:店内外充满了快活的空气。
听人家背地里谈论,孔乙己原来也读过书,但终于没有进学⑼,又不会营生⑽;于是愈过愈穷,弄到将要讨饭了。幸而写得一笔好字,便替人家钞⑾钞书,换一碗饭吃。可惜他又有一样坏脾气,便是好喝懒做。坐不到几天,便连人和书籍纸张笔砚,一齐失踪。如是几次,叫他抄书的人也没有了。孔乙己没有法,便免不了偶然做些偷窃的事。但他在我们店里,品行却比别人都好,就是从不拖欠;虽然间或没有现钱,暂时记在粉板上,但不出一月,定然还清,从粉板上拭去了孔乙己的名字。
孔乙己喝过半碗酒,涨红的脸色渐渐复了原,旁人便又问道,"孔乙己,你当真认识字么?"孔乙己看着问他的人,显出不屑置辩的神气。他们便接着说道,"你怎的连半个秀才也捞不到呢?"孔乙己立刻显出颓唐不安模样,脸上笼上了一层灰色,嘴里说些话;这回可是全是之乎者也之类,一些不懂了。在这时候,众人也都哄笑起来:店内外充满了快活的空气。
在这些时候,我可以附和着笑,掌柜是决不责备的。而且掌柜见了孔乙己,也每每这样问他,引人发笑。孔乙己自己知道不能和他们谈天,便只好向孩子说话。有一回对我说道,"你读过书么?"我略略点一点头。他说,"读过书,......我便考你一考。茴香豆的茴字,怎样写的?"我想,讨饭一样的人,也配考我么?便回过脸去,不再理会。孔乙己等了许久,很恳切的说道,"不能写罢?......我教给你,记着!这些字应该记着。将来做掌柜的时候,写账要用。"我暗想我和掌柜的等级还很远呢,而且我们掌柜也从不将茴香豆上账;又好笑,又不耐烦,懒懒的答他道,"谁要你教,不是草头底下一个来回的回字么?"孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说,"对呀对呀!......回字有四样写法⑿,你知道么?"我愈不耐烦了,努着嘴走远。孔乙己刚用指甲蘸了酒,想在柜上写字,见我毫不热心,便又叹一口气,显出极惋惜的样子。
有几回,邻居孩子听得笑声,也赶热闹,围住了孔乙己。他便给他们茴香豆吃,一人一颗。孩子吃完豆,仍然不散,眼睛都望着碟子。孔乙己着了慌,伸开五指将碟子罩住,弯腰下去说道,"不多了,我已经不多了。"直起身又看一看豆,自己摇头说,"不多不多!多乎哉?不多也⒀。"于是这一群孩子都在笑声里走散了。
孔乙己是这样的使人快活,可是没有他,别人也便这么过。
有一天,大约是中秋前的两三天,掌柜正在慢慢的结账,取下粉板,忽然说,"孔乙己长久没有来了。还欠十九个钱呢!"我才也觉得他的确长久没有来了。一个喝酒的人说道,"他怎么会来?......他打折了腿了。"掌柜说,"哦!""他总仍旧是偷。这一回,是自己发昏,竟偷到丁举人家里去了。他家的东西,偷得的么?""后来怎么样?""怎么样?先写服辩⒁,后来是打,打了大半夜,再打折了腿。""后来呢?""后来打折了腿了。""打折了怎样呢?""怎样?......谁晓得?许是死了。"掌柜也不再问,仍然慢慢的算他的账。
中秋之后,秋风是一天凉比一天,看看将近初冬;我整天的靠着火,也须穿上棉袄了。一天的下半天,没有一个顾客,我正合了眼坐着。忽然间听得一个声音,"温一碗酒。"这声音虽然极低,却很耳熟。看时又全没有人。站起来向外一望,那孔乙己便在柜台下对了门槛坐着。他脸上黑而且瘦,已经不成样子;穿一件破夹袄,盘着两腿,下面垫一个蒲包,用草绳在肩上挂住;见了我,又说道,"温一碗酒。"掌柜也伸出头去,一面说,"孔乙己么?你还欠十九个钱呢!"孔乙己很颓唐的仰面答道,"这......下回还清罢。这一回是现钱,酒要好。"掌柜仍然同平常一样,笑着对他说,"孔乙己,你又偷了东西了!"但他这回却不十分分辩,单说了一句"不要取笑!""取笑?要是不偷,怎么会打断腿?"孔乙己低声说道,"跌断,跌,跌......"他的眼色,很像恳求掌柜,不要再提。此时已经聚集了几个人,便和掌柜都笑了。我温了酒,端出去,放在门槛上。他从破衣袋里摸出四文大钱,放在我手里,见他满手是泥,原来他便用这手走来的。不一会,他喝完酒,便又在旁人的说笑声中,坐着用这手慢慢走去了。
自此以后,又长久没有看见孔乙己。到了年关⒂,掌柜取下粉板说,"孔乙己还欠十九个钱呢!"到第二年的端午,又说"孔乙己还欠十九个钱呢!"到中秋可是没有说,再到年关也没有看见他。
我到现在终于没有见------大约孔乙己的确死了。 [2]
一九一九年三月。
我们来提取其中关于 孔乙己 的信息:
python
import re
with open("./kyj.txt", 'r', encoding='utf-8') as f:
text = f.read()
# 孔乙己出现的次数.
result_nums = re.findall(r'孔乙己', text, re.S)
print(result_nums)
# 使用dir()可以查看到对象可调用的方法
print(dir(result_nums))
# 因为该对象有__len__方法, 所以可以使用
print(len(result_nums))
运行结果是:
text
['孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己', '孔乙己']
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
33
将上述re.findall()改为 re.finditer()看看有何变化:
python
import re
with open("./kyj.txt", 'r', encoding='utf-8') as f:
text = f.read()
"""
I: 忽略大小写.
M: 拆分行, 多行匹配. 这个是影响^$.
S: 忽略换行符. 也可以理解为使"."匹配所有字符.
X: 添加注释, 使正则表达式便于理解.
DOTALL: 等价于S.
U: python3不用了.
"""
result_list = re.finditer(r'孔乙己', text, re.S)
print(result_list)
print(dir(result_list))
for result in result_list:
print(result.group())
运行结果是:
text
<callable_iterator object at 0x7fe9e80f84f0>
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
孔乙己
孔乙己
孔乙己
...
ok, 看看下面的代码:
python
import re
string = 'testesttestest'
test_count = re.findall(r'test', string)
print(test_count)
print(len(test_count))
运行结果是:
text
['test', 'test']
2
可能有的老铁会说为什么不是 4 呢?
这是因为 正则表达式的引擎是一直朝后面找, 不会再向前匹配.
但是这里扩展一下, 有个第三方库 regex, 它是对 re 的扩展, 里面实现了重叠匹配机制:
python
import regex
string = 'testesttestest'
# overlapped 重叠匹配.
test_count = regex.findall(r'test', string, overlapped=True)
print(test_count)
print(len(test_count))
运行结果是:
text
['test', 'test', 'test', 'test']
4
如果将 regex.findall(overlapped=True) 改为 regex.findall(overlapped=False) , 就实现了和 re.findall()一样的功能, 都是不会进行重叠匹配的.
可选字符
python
import re
# 问号的含义: 子表达式零次或一次
# 加号的含义: 子表达式一次或多次
# 星号的含义: 子表达式零次或多次
string1 = 'color'
string2 = 'colour'
for text in [string1, string2]:
regex = re.search(r'colou?r', text)
if regex:
print(regex.group())
else:
print('未匹配到')
运行结果是:
text
color
colour
如果改为 + 号 regex = re.search(r'colou+r', text), 看看执行结果:
text
未匹配到
colour
枚举
[]: 枚举, 匹配括号里出现的字符[^]: 枚举取反, 匹配括号里出现的字符之外的字符- \ : 转义符
- 加号的含义: 子表达式一次或多次
python
# 正则里有含义的特殊符号.
string1 = '1114564678122-11212]'
regex1 = re.search(r'[0-9]+', string1)
print(regex1.group())
运行结果是:
text
1114564678122
如果想匹配所以呢, 包括后面的 -11212]:
python
# 正则里有含义的特殊符号.
string1 = '1114564678122-11212]'
regex1 = re.search(r'[0-9-\]]+', string1)
print(regex1.group())
运行结果是:
text
1114564678122-11212]
关于 特殊枚举, 比如中文相关的:
说明: 中文字符集的第一个字编码和最后一个字的编码范围是:
[\u4e00-\u9fa5]
python
# 特殊枚举: 中文 [\u4e00-\u9fa5]
string2 = '长风破浪会有时,直挂云帆济沧海.'
# \s: 所有空白符,包括换行符
regex2 = re.search(r'[\u4e00-\u9fa5,\s\.]+', string2)
print(regex2.group())
运行结果是:
text
长风破浪会有时,直挂云帆济沧海.
关于枚举取反, 比例这里的匹配中文之外的所有字符:
python
# 枚举取反.
string3 = '正则表达式是一种高级文本处理技术(Regular expression is an advanced text processing technology)'
regex3 = re.search(r'[^\u4e00-\u9fa5]+', string3)
print(regex3.group())
运行结果是:
text
(Regular expression is an advanced text processing technology)
或
|: 或
(): 分组
前面我们有讲到 枚举, 我们看看下面的代码用枚举执行的情况:
python
import re
text_list = [
'wlb.avi',
'wlb.mp4',
'wlb.jpg',
'wlb.zip',
'wlb.txt',
'wlb.xls',
]
for text in text_list:
regex = re.search(r'wlb\.[avimp4jgztxls]+', text)
if regex:
print(regex.group())
运行结果是:
text
wlb.avi
wlb.mp4
wlb.jpg
wlb.zip
wlb.txt
wlb.xls
我们看到这里用枚举来做也是能得到结果的, 但是实际网页中的数据不一定都是对的, 可能会出现错误的脏数据, 比如把后缀.mp4错写成了.mpt文件:
python
import re
text_list = [
'wlb.avi',
'wlb.mp4',
'wlb.jpg',
'wlb.zip',
'wlb.txt',
'wlb.xls',
'wlb.mpt',
]
for text in text_list:
regex = re.search(r'wlb\.[avimp4jgztxls]+', text)
if regex:
print(regex.group())
这个时候用枚举的话也是查到了脏数据:
text
wlb.avi
wlb.mp4
wlb.jpg
wlb.zip
wlb.txt
wlb.xls
wlb.mpt
所以这里我们使用 或 来对上面的代码进行改写:
python
import re
text_list = [
'wlb.avi',
'wlb.mp4',
'wlb.jpg',
'wlb.zip',
'wlb.txt',
'wlb.xls',
'wlb.mpt',
]
for text in text_list:
regex = re.search(r'wlb\.(avi|mp4|jpg|zip|txt|xlsx?)', text)
if regex:
print(regex.group())
运行结果是:
text
wlb.avi
wlb.mp4
wlb.jpg
wlb.zip
wlb.txt
wlb.xls
这里我们补充说明一下 或 的执行机制:
python
text2 = '世界知名逆向工程师刘老板'
result = re.search(r'世界知名逆向工程师(刘老|刘|刘老板)', text2)
print(result.group())
运行结果是:
text
世界知名逆向工程师刘老
所以, 或的执行机制是: 找到了想要的数据时就不会再继续向后匹配了.
分组
python
import re
text_list = ['2022-06-12', '2021 07 08', '2019/05/06']
# 匹配年月日, **单独** 把年,月,日拿出来.
for text in text_list:
regex = re.search(r'(\d{4})[-\s/](\d{2})[-\s/](\d{2})', text)
print(regex.group(1))
print(regex.group(2))
print(regex.group(3))
print('=' * 50)
运行结果是:
text
2022
06
12
==================================================
2021
07
08
==================================================
2019
05
06
==================================================
非捕获组
- (?😃: 非捕获组
非捕获组在数据清洗工作中用的还是很多的.
下面有一个需求: 只要域名, 不要协议.
python
import re
text1_list = [
'https://www.baidu.com/',
'http://www.baidu.com/'
]
for text1 in text1_list:
regex = re.search(r'(https://|http://)(www.*?$)', text1)
print(regex.group(1))
print(regex.group(2))
运行结果是:
text
https://
www.baidu.com/
http://
www.baidu.com/
下面我们用 非捕获组 对上面的代码进行改写:
python
text1_list = [
'https://www.baidu.com/',
'http://www.baidu.com/'
]
for text1 in text1_list:
regex = re.search(r'(?:https://|http://)(www.*?$)', text1)
print(regex.group(1))
这是 协议 那组数据就不会被捕获, 所以域名就是第一组.
分组的引用
这也是比较常见的用法, 看看下面的代码:
python
# 分组的引用
text2_list = [
'<div>王老板</div>',
'<div>王老板</img>'
]
# 需求,只要正确的标签.
for text2 in text2_list:
regex = re.search(r'<(\w+)>[\u4e00-\u9fa5]+</\1>', text2)
print(regex.group())
注意对上面代码中 </\1>的理解, 就是 第一个分组的引用.
所以, 这里的运行结果是:
text
<div>王老板</div>
AttributeError: 'NoneType' object has no attribute 'group'
现在还有一个需求: 不管下面这个人是谁, 都要他是永远的厉害.
python
text3_list = [
'王老板永远的厉害',
'张老板永远的无敌',
'李老板永远的爹'
]
# 需求: 我不管这个人是谁 ,我都要他是永远的厉害.
for text3 in text3_list:
# re.sub(): 替换
regex = re.sub(r'(.老板永远的)(.*?)$', r'\1厉害', text3)
print(regex)
运行结果是:
text
王老板永远的厉害
张老板永远的厉害
李老板永远的厉害
断言
正则表达式的引擎, 匹配过的地方叫后, 未匹配过的地方叫前.
比如: 我爱你, 现在查到了 爱, 那么 我 就是后(匹配过的), 你 就是前(未匹配的).
后顾
匹配过的地方叫后.
python
# 后顾(?<=), 负后顾(?<!) 从右往左看.
# 前瞻后顾帮助正则判断, 不匹配. 不会把前瞻后顾的值给你. 可以理解为 非捕获.
text_list = [
'美女请王老板吃饭',
'畜生请王老板吃饭',
'妹妹请王老板吃饭',
'卞大请王老板吃饭',
]
# 我们不想要畜生来请王老板吃饭, 所以这里是负后顾.
for text in text_list:
regex = re.search(r'(?<!畜生)请王老板吃饭', text)
# 或者也可以这样写:
# regex = re.search(r'(?<=美女|妹妹|卞大)请王老板吃饭',text)
if regex:
print(regex.group())
else:
print(f'没匹配到: {text}')
运行结果是:
text
请王老板吃饭
没匹配到: 畜生请王老板吃饭
请王老板吃饭
请王老板吃饭
如果我们只要 妹妹请王老板吃饭, 那么可以这样改写代码:
python
regex = re.search(r'(?<=妹妹)请王老板吃饭', text)
注意后顾有一个坑就是:
比如前面的代码中:
python
regex = re.search(r'(?<=美女|妹妹|卞大)请王老板吃饭',text)
这里的 美女、妹妹、卞大 长度是一样的, 是没有问题的, 如果长度不一致, 会出现异常报错:
python
regex = re.search(r'(?<=美女子|妹妹好|卞大)请王老板吃饭',text)
这样是不对的, 所以我们在用后顾的时候, 要特别注意这一点.
前瞻
未匹配过的地方叫前.
比如这里有一个需求: 只允许国内的网站注册.
python
# 前瞻(?=), 负前瞻(?!) 从左往右看.
text2_list = [
'aaabbbccc@qq.com',
'aaabbbccc@163.com',
'aaabbbccc@gmail.com',
]
# 需求: 只需要国内的网站注册.
for text2 in text2_list:
# regex = re.search(r'\w+@(?=qq|163).*?$', text2)
regex = re.search(r'\w+@(?!gmail).*?$', text2)
if regex:
print(regex.group())
现在增加难度, 需求是: 切分公司, 要求括号里的不切.
python
text3 = '王老板有限公司,a.b集团有限公司.拜读有限公司(曾用名ccc有限公司,co,.ltd),yrx有限公司.'
name_list = re.split(r'(?<![a-z,])[,.](?![^\(]*\))', text3)
print(name_list)
运行结果是:
text
['王老板有限公司', 'a.b集团有限公司', '拜读有限公司(曾用名ccc有限公司,co,.ltd)', 'yrx有限公司', '']
回溯
回溯指的是 正则表达式的执行机制, 过程, 这里只做了解.
python
string = 'aaaaab'
# 同样的结果, 性能有差距.
regex1 = re.compile(r'a+b') # 0次回溯.
regex2 = re.compile(r'a.*ab') # 2次回溯.
print('1:', regex1.search(string).group())
print("2:", regex2.search(string).group())
为什么 r'a+b' 是0次回溯:
因为执行步骤是先执行 a, 然后执行+, 找到 aaaa, 然后匹配到b, 没有回溯.
为什么 r'a.*ab' 是2次回溯:
因为先 执行 a, 然后执行 .*找到后面所有的字符, 也就是aaaab, 后面执行的a和b会有2次回溯.
|----------------------|
| 遇见安然遇见你,不负代码不负卿。 |
| 谢谢老铁的时间,咱们下篇再见~ |