Python-正则表达式
- 正则表达式的含义
- 使用正则表达式步骤
-
- 寻找规律
- 表示规律
-
- 普通字符匹配
- 元字符匹配
-
- 数量词
- 指代字符
- 边界相关
- 在[]中的元字符
- 正则表达式分组
-
- 正则表达式常用方法
-
- re.match函数
-
- match参数
- flags参数
- re.match匹配对象方法
- groupdict应用
- re.search方法
-
- re.match与re.search的区别
- re.match和re.search带不带圆括号的区别
- re.findall
-
- re.finditer
-
- re.findall,re.finditer与re.match,re.search的区别
- re.sub
- re.subn
- re.split
- 贪婪非贪婪
- 正则表达式应用举例
- 正则表达式参考网站
正则表达式的含义
- 正则表达式是对字符串操作的一种逻辑公式,是用事先定义好的一些特殊字符及其组合,组成一个规则字符串,这个规则字符串用来表达对字符串的一种过滤逻辑。
- 正则表达式是用来匹配与查找,替换字符串的。
举个例子:
从字符串"1234jfvpsjgmbk780a15801879957**82mmn1234lciclshalcojfbg…"里找出手机号码,这个字符串可能有上万个字符,不可能人眼一个个找,使用正则表达式,告诉它,凡是158,138,182等开头,并且连续11个都是数字的,就是手机号码。那么凡是158,138,182等开头,并且连续11个都是数字的,就是手机号码,这段描述就是正则表达式的文字描述,接下来,我们要把它转化为专业的python描述。
使用正则表达式步骤
- 寻找规律
- 使用正则符号表示规律
- 提取信息,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败
寻找规律
- 规律要进行发现和提炼,抓住主要特征
- 比如上述的电话号码的描述就是规律
表示规律
- 要用代码来表示规律,需要使用re模块,相关的正则中的模式字符串的一些要求如下:
普通字符匹配
import re
result = re.findall("alexsel","gtuanalesxalexselericapp")
print(result)
result:
['alexsel']
元字符匹配
[]
- []中的字符是任选择一个字符。
- 如果所选字符是ASCll码中连续的一组的一个,那么可以使用"-"字符连接。
- 例如[0-9]表示0-9的其中一个数字,[A-Z]表示A-Z的其中一个大写字符,[0-9A-Z]表示0-9的其中一个数字或者A-Z的其中一个大写字符。
- [ABC]表示A,B,C中任意一个字符。
- []方括号内可以使用指代字符表中的任意一个。
import re
result = re.findall("[od]", "Hello,World.")
print(result)
result:
['o', 'o', 'd']
()
re.findall("(ab)+","aabz1144cabcd")
result:
['ab', 'ab']
{}
- {} :控制它前面一个字符的匹配个数,可以有区间(闭区间),有区间的情况下按照多的匹配。
- 精确匹配 n 个前面表达式, 例如o{2} 不能匹配 “job” 中的 “o”,但是能匹配 “food” 中的两个 o
re.findall("alexsel{3}","aaaalexselllll")
result:
['alexselll']
数量词
- 正则表达式的重复限定符,用于指定一个模式的重复次数
数量词 |
含义 |
举例 |
结果 |
符号 * |
匹配前一个字符0次或无限次 |
re.findall(“lo*”, “Hello,World.”) |
[‘l’, ‘lo’, ‘l’] |
符号 + |
匹配前一个字符1次或无限次 |
re.findall(“lo+”, “Hello,World.”) |
[‘lo’] |
符号? |
匹配前一个字符0次或1次 |
re.findall(“lo?”, “Hello,World.”) |
re.findall(“lo?”, “Hello,World.”) |
{m} |
匹配前一个字符m次 |
re.findall(“lo{1}”, “loooxooloox”) |
[‘lo’, ‘lo’] |
{m,} |
前一个字符至少出现m次 |
re.findall(“lo{2,}”, “loooxooloolox”) |
[‘looo’, ‘loo’],贪婪方式 |
{m,n} |
匹配前一个字符m~n次 |
re.findall(“lo{2,3}”, “loooxooloolox”) |
[‘looo’, ‘loo’], 贪婪方式 |
指代字符
- 正则表达式的预定义字符类,用于匹配常见字符集合
- 用指代字符可以指代某一类的字符,\小写和\大写一般相反
- 因为.在正则表达式中有特殊含义(匹配任意一个字符,除了换行符),如果要匹配正常的.,使用r"."或者 “\.”
字符 |
含义 |
举例 |
结果 |
\d |
匹配任何十进制数字;相当于类 [0-9] |
re.findall(r"a\d{3,}", “bca12345a78a985”) |
[‘a12345’, ‘a985’] |
\D |
与 \d 相反,匹配任何非十进制数字的字符;相当于类 [^0-9] |
re.findall(r"a\D{2}", “bca12345a78a985amdax”) |
[‘amd’] |
\w |
匹配字母数字及下划线,相当于[a-zA-Z0-9_] |
re.findall(r"\w\d{2}", “hello95aa2d78”) |
[‘o95’, ‘d78’] |
\W |
与 \w 相反 |
|
|
\s 匹配任何空白字符 |
(包含空格、换行符、制表符等);相当于类 [ \t\n\r\f\v] |
re.findall(r"\s\w", “hello world hello \nChina”) |
[’ w’, ’ h’, ‘\nC’] |
\S |
与 \s 相反,匹配任何非空白字符;相当于类 [^ \t\n\r\f\v] |
|
|
. |
匹配一个除了换行符任意一个字符 |
|
|
\\ |
原意的\ |
|
|
[^] |
相当于非运算符,除了后面的,其它的都行 |
|
|
边界相关
- \b注意本身在字符串中会转义,如果要匹配\b,在正则表达式中,要么用r"\b",要么用"\\b"
字符 |
含义 |
举例 |
结果 |
\b |
匹配单词的开始或结束,即单词的边界,boundary |
re.findall(r"\babc\b",“abc sds abc abcd”) |
[‘abc’, ‘abc’] |
\B |
与 \b 相反 |
re.findall(r"\Babc\B",“abc sds abc cabcd”) |
[‘abc’] |
\A |
从字符串的开始处匹配,A是字母里的第一个,用于指示开始处 |
re.findall(r"\Aabc",“abc sds abc abcd”) |
[‘abc’] |
\Z |
从字符串的结束处匹配,如果存在换行,只匹配到换行前的结束字符串,Z是字母里的末尾,用于指示结束处 |
re.findall(“oo\w\Z”,“ood123foozaoob”) |
[‘oob’] |
^ |
匹配开头,只有后面跟的字符串在开头,才能匹配上 |
re.findall(“^alexsel”,“alexselgtaassiqialexsel124”) |
[‘alexsel’] |
$ |
匹配末尾,只有它前面的字符串在检测的字符串的最后,才能匹配上 |
re.findall(“alexsel$”,“alexselgtaassiqialexsel”) |
[‘alexsel’] |
在[]中的元字符
- 大部分元字符在[]中就表示普通字符,无特殊意义。
- 但是- ^ \具有特殊意义。
re.findall("a[.]d","aaaacd")
result:
[]
字符 |
含义 |
举例 |
结果 |
[-] |
说明匹配字符范围,如[a-z]表示a到z的字符中的任意一个 |
re.findall(“[a-d]\d”, “aaaacd34”) |
[‘d3’] |
[^] |
匹配[]除了后面所跟范围的字符,(^在这里有 非 的意思) |
re.findall(“[^1-4]\w”,“aaazz1111344444c446”) |
[‘aa’, ‘az’, ‘z1’, ‘c4’], |
正则表达式分组
- 分组可以让我们从文本内容中提取指定模式的部分内容,用()来表示要提取的分组。
- 分组是在整个正则表达式筛选完之后的进一步筛选。
- 分组中可以只用|字符。
- 分组中可以使用\num进行引用。
- 分组中可以使用\名称方式进行引用。
分组中用到的特殊字符
字符 |
功能 |
(abc) |
将括号中字符作为一个分组 |
(|) |
匹配左右任意一个表达式 |
(\num) |
引用分组num |
(?P) |
分组取别名 |
(?P=name) |
引用别名为name的分组 |
捕获组
- 可以给正则表达式的子组起一个名字,表达该子组的意义。这种有名称的子组即为捕获组。
取名字格式:(?P<name>pattern)
引用格式:(?P=name)-,不必再详细描述pattern
如果要取得该捕获组的内容,可以.group("name")
分组举例
import re
html = """"""
result = re.search(r"<(\w+) type=(.+) src=(.+)>\1>", html)
r2 = result.group(2)
r3 = result.group(3)
print(result)
print(r2, r3)
result:
<re.Match object; span=(0, 82), match='<script type="text/javascript" src=\'//icws.jb51.>
"text/javascript" '//icws.jb51.net/good2021/arc2019.js'
import re
html = "Shanghai
"
result = re.search("<(?P\w+)><(?P\w+)>(.+)(?P=name2)>(?P=name1)>", html)
r3 = result.group(3)
print(result)
print(r3)
result:
<re.Match object; span=(0, 30), match='Shanghai
'>
Shanghai
正则表达式常用方法
re.match函数
- re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match() 就返回 none。
- 这个方法并不是完全匹配。它仅仅决定在字符串开始的位置是否匹配。所以当pattern结束时若还有剩余字符,仍然视为成功。
- 想要完全匹配,可以在表达式末尾加上边界匹配符$
re.match(pattern, string, flags=0)
match参数
参数 |
描述 |
pattern |
匹配的正则表达式 |
string |
要匹配的字符串 |
flags |
标志位,用于控制正则表达式的匹配方式。如:是否区分大小写,多行匹配等等。 |
flags参数
参数 |
描述 |
re.I |
忽略大小写 |
re.L |
表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境 |
re.M |
多行模式 |
re.S |
即为 . 并且包括换行符在内的任意字符(. 不包括换行符) |
re.U |
表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库 |
re.X |
为了增加可读性,忽略空格和 # 后面的注释 |
re.match匹配对象方法
匹配对象方法 |
描述 |
group(num=0) |
分组捕获,匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组 |
groups() |
返回一个包含所有小组字符串的元组,从1到 所含的小组号 |
span() |
返回匹配到内容的下标范围,元组形式 |
start() |
返回匹配到内容的开始位置 |
end() |
返回匹配到内容的结束位置 |
groupdict() |
将命名后的子组以字典形式返回 |
import re
result =re.match("oo\w{10}","ood123foozaoob")
print(result)
print(result.group())
result:
<re.Match object; span=(0, 12), match='ood123foozao'>
ood123foozao
groupdict应用
- 返回一个字典,包含所有经命名的匹配子群,键值是子群名。
code:
import re
line = "Cats are smarter than dogs"
match_obj = re.match(r"(?P.*) are (?P.*?) (?P.{2})", line, re.M | re.I)
if match_obj:
print("match_obj.group() : ", match_obj.group())
print("match_obj.group(1) : ", match_obj.group(1))
print("match_obj.group(2) : ", match_obj.group(2))
print("match_obj.group(3) : ", match_obj.group(3))
print(match_obj.groupdict())
print(match_obj.groups())
else:
print("No match!!")
result:
match_obj.group() : Cats are smarter th
match_obj.group(1) : Cats
match_obj.group(2) : smarter
match_obj.group(3) : th
{'first': 'Cats', 'second': 'smarter', 'third': 'th'}
('Cats', 'smarter', 'th')
re.search方法
- re.search 扫描整个字符串并返回第一个成功的匹配
- 参数说明同re.match
re.search(pattern, string, flags=0)
code:
import re
line = "Cats123are45smarter78than<>dogs"
search_obj = re.search(r"(?P\d{2})are(?P.+)(?P.{2})", line, re.M | re.I)
if search_obj:
print("search_obj.group() : ", search_obj.group())
print("search_obj.group(1) : ", search_obj.group(1))
print("search_obj.group(2) : ", search_obj.group(2))
print("search_obj.group(3) : ", search_obj.group(3))
print(search_obj.groupdict())
else:
print("No match!!")
result:
search_obj.group() : 23are45smarter78than<>dogs
search_obj.group(1) : 23
search_obj.group(2) : 45smarter78than<>do
search_obj.group(3) : gs
{'first': '23', 'second': '45smarter78than<>do', 'third': 'gs'}
re.match与re.search的区别
- re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None
- re.search匹配整个字符串,直到找到一个匹配
re.match和re.search带不带圆括号的区别
- 找到的内容是否分组
- 不带圆括号,返回值通过group方法取出是整个表达式所匹配到的内容
- 带有圆括号,返回值通过group方法带序号的形式可以取出各个圆括号分组中的内容
re.findall
- 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。
- 参数说明同re.match。
re.findall(pattern, string, flags=0)
import re
text = "https://mp.csdn.net/postedit/828posd65219"
result = re.findall(r"pos[a-z]+", text)
print(result)
result:
['postedit', 'posd']
import re
result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
print(result)
result:
返回元组列表,元组里是分组的各项内容
[('width', '20'), ('height', '10')]
re.findall带不带圆括号的区别
- 不带圆括号,其输出的内容就是整个表达式所匹配到的内容。
- 带有1个圆括号,其输出的内容就是括号匹配到的内容,而不是整个表达式所匹配到的结果。
- 带有2个圆括号,输出是一个list中包含2个tuple,2个tuple中的内容分别为2个圆括号匹配到的内容。更多括号类似,只是tuple的元素增加。
import re
string = "abcdefg acbdgef fedcfe cadbgfe"
result0 = re.findall("\w+\s+\w+",string)
result1 = re.findall("(\w+)\s+\w+",string)
result2 = re.findall("(\w+)\s+(\w+)",string)
print(result0)
print(result1)
print(result2)
result:
['abcdefg acbdgef', 'fedcfe cadbgfe']
['abcdefg', 'fedcfe']
[('abcdefg', 'acbdgef'), ('fedcfe', 'cadbgfe')]
re.finditer
- 和findall 类似,在字符串中找到正则表达式所匹配的所有子串,返回一个match对象的迭代器。
- 获取匹配结果需要调用match对象的group()、groups或group(index)方法
- 参数说明同re.match。
re.finditer(pattern, string, flags=0)
import re
result = re.finditer(r"\d+\w?", "12a32bc43jf3")
print(result)
print(result.__next__())
print(result.__next__())
for i_result in result:
print(i_result.group())
result:
<callable_iterator object at 0x000002EE5A2D6E48>
<re.Match object; span=(0, 3), match='12a'>
<re.Match object; span=(3, 6), match='32b'>
43j
3
re.findall,re.finditer与re.match,re.search的区别
- re.match,re.search匹配上一个就返回,而re.findall,re.finditer搜索到最后,把整个字符串都遍历一遍。
- re.match,re.search返回的是对象,通过group方法可以返回匹配到的内容或分组内容。
- re.findall返回的是列表,匹配到的分组内容是以列表元素(元组)给出的。
- re.finditer返回一个match对象的迭代器。
re.sub
- re.sub用于替换字符串中的匹配项, 返回替换后的字符串。
- flags的描述见re.match。
re.sub(pattern, repl, string, count=0, flags=0)
参数 |
描述 |
pattern |
正则中的模式字符串 |
repl |
替换的字符串,也可为一个函数,如果是函数的时候,将match对象传给它,返回一个字符串 |
string |
要被查找替换的原始字符串 |
count |
模式匹配后替换的最大次数,默认 0 表示替换所有的匹配 |
flags |
标志位,用于控制正则表达式的匹配方式。如:是否区分大小写,多行匹配等 |
import re
phone = "2004-959-559 # 这是一个国外电话号码"
num1 = re.sub(r'-|#|\s.*$', "", phone, 2)
print("电话号码是: ", num1)
num2 = re.sub(r'-|#|\s.*$', "", phone)
print("电话号码是: ", num2)
result:
电话号码是: 2004959559
电话号码是: 2004959559
import re
def func(obj_match):
result = int(obj_match.group()) + 20
return str(result)
score = "Math:90, English:30"
num1 = re.sub(r"\d+", func, score)
print("作弊成绩是: ", num1)
result:
作弊成绩是: Math:110, English:50
re.subn
- subn和sub类似,不同之处在于subn()还返回一个表示替换的总数。
- 替换后的字符串和表示替换总数的数字一起作为一个拥有两个元素的元组返回。
import re
phone = "2004-959-559 # 这是一个国外电话号码"
num1 = re.subn(r'-|#|\s.*$', "", phone, 2)
print("电话号码是: ", num1)
num2 = re.subn(r'-|#|\s.*$', "", phone)
print("电话号码是: ", num2)
result:
电话号码是: ('2004959559 # 这是一个国外电话号码', 2)
电话号码是: ('2004959559 ', 3)
re.split
- re.split方法按照能够匹配的子串将字符串分割后返回列表。
- maxsplit 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。
- 当pattern中带有圆括号时,将圆括号中的内容也添加在列表中返回。
re.split(pattern, string[, maxsplit=0, flags=0])
import re
phone = "2004-959-559 # 这是一个国外电话号码"
result1 = re.split(r'-|#', phone)
print(result1)
result2 = re.split(r'-|#', phone, 2)
print(result2)
result3 = re.split(r'\d(-|#)', phone)
print(result3)
result:
['2004', '959', '559 ', ' 这是一个国外电话号码']
['2004', '959', '559 # 这是一个国外电话号码']
['200', '-', '95', '-', '559 # 这是一个国外电话号码']
贪婪非贪婪
- Python里数量词默认是贪婪的(在少数语言里默认非贪婪),总是尝试匹配尽可能多的字符。
- 非贪婪则相反,总是尝试尽可能少的字符。
- 在 数量词*, ?, +,{m,n}系列 后面 加上?,则贪婪变成非贪婪。
code:
import re
msg = "hello123456hello"
result1 = re.search("hello\d+", msg)
result2 = re.search("hello\d+?", msg)
result3 = re.search("hello\d*?", msg)
print(result1.group())
print(result2.group())
print(result3.group())
result:
hello123456
hello1
hello
正则表达式应用举例
import re
phone = "15800458778"
result1 = re.match("[1]\d{9}[^47]$",phone)
print(result1)
result:
<re.Match object; span=(0, 11), match='15800458778'>
- 从网上爬一个照片下来
- 在网上先搜好图片,选中图片,右击鼠标选择检查。
- 将该图片的相关源码拷贝下来。
- 找到src中的内容,通过requests模块读取写入本地。
import re
import requests
html = """
"""
result = re.search(r'.*src="(.+?)"', html)
image_path = result.group(1)
req = requests.get(image_path)
with open("chenxiao.jpg", "wb") as fs:
fs.write(req.content)
正则表达式参考网站
- 此网站可以直观的显示正则表达式匹配的结果。
https://deerchao.cn