什么是正则表达式
正则表达式即regular expression,使用来简洁的表示字符串的表达式;
正则表达式的作用
正则表达式用来检索或替换符合某些规则的文本,如在编写爬虫爬取网页数据时,对于html的解析或者说数据的提取,去要使用正则表达式进行定位;
正则表达式的表示类型
- raw string(原生字符串类型):即字符串中不包含转义符,即\不被解释为转移符
如r’[1-9]\d{5}’,r’\d{3}-\d{8}|\d{4}-\d{7}’,需要在字符串前加r; - 普通字符串类型:比较繁琐,含转义符如’[1-9]\d{5}’,‘\d{3}-\d{8}|\d{4}-\d{7}’;
正则表达式的使用规则
正则表达式的常用操作符如下
操作符 | 说明 | 举例 |
---|---|---|
. | 表示任何单个字符 | |
[ ] | 字符集,对单个字符给出取值范围 | [abc]表示a、b、c,[a-z]表示a到z中间的单个字符 |
[^ ] | 非字符集对单个字符给出排除范围 | [^abc]表示非a或b或c的单个字符 |
* | 前一个字符0次或无限次扩展 | abc*表示ab、abc、abcc、abccc等 |
+ | 前一个字符1次或无限次扩展 | abc+表示abc、abcc、abccc等 |
? | 前一个字符0次或1次扩展 | abc?表示ab、abc |
| | 左右表达式任意一个 | abc|def表示abc、def |
{m} | 扩展前一个字符m次 | ab{2}c表示abbc |
{m,n} | 扩展前一个字符m至n次(含n) | ab{1,2}c表示abc、abbc |
^ | 匹配字符串开头 | ^abc表示abc且在一个字符串的开头 |
$ | 匹配字符串结尾 | abc$表示abc且在一个字符串的结尾 |
( ) | 分组标记,内部只能使用|操作符 | (abc)表示abc,(abc|def)表示abc或def |
\w | 表示除换行符以外的所有字符 | |
\s | 匹配任意的空白符 | |
\d | 匹配数字 | |
\b | 匹配单词的开始或结束 |
下面给除一些经典的正则表达式实例
表达式 | 实例 |
---|---|
^[A-Za-z] | 由26个字母组成的字符串 |
^[A-Za-z0-9]+$ | 由26个字母和数字组成的字符串 |
^-?\d+$ | 整数形式的字符串 |
^[0-9]*[1-9][0-9]*$ | 正整数形式的字符串 |
[1-9]\d{5} | 中国境内邮政编码,6位 |
[\u4e00-\u9fa5] | 匹配中文字符 |
\d{3}-\d{8}|\d{4}-\d{7} | 国内电话号码,010-68913536 |
贪婪匹配与懒惰匹配
RE库默认采用贪婪匹配,即输出匹配正则表达式的最长的子串
import re
match=re.search(r'PY.*N','PYANBNCNDN')
print(match.group(0))
# ->PYANBNCNDN
import re
pattern=r'.*shop'
pattern2=r'.*?shop'
string='mr______________shop mr_shop'
print(re.match(pattern,string))
print(re.match(pattern2,string))
# -><re.Match object; span=(0, 28), match='mr______________shop mr_shop'>
# -><re.Match object; span=(0, 20), match='mr______________shop'>
在上面的程序中,PYAN、PYANBN、PYANBNCN、PYANBNCNDN都是可以,它遵循贪婪匹配,得到了最长的字串;
如果是要得到最短的正则表达式匹配的串呢,只需要将程序中的.*
改为.*?
即可
事实上,当操作符可以匹配不同长度的串的时候,我们都可以在之后加一个?
来获得懒惰匹配;
如下表一些常用贪婪匹配操作符
操作符 | 说明 |
---|---|
*? | 前一个字符0次或无限次扩展,懒惰匹配 |
+? | 前一个字符1次或无限次扩展,懒惰匹配 |
?? | 前一个字符0次或1次匹配,懒惰匹配 |
{m,n}? | 扩展前一个字符m至n次(含n),懒惰匹配 |
Re库
下面具体说一下这几个Re库的函数;
- re.complie(pattern, flags=0):编译正则表达式模式,
返回一个对象的模式
即pattern,一般用来预编译,用来提高效率,pattern为一个正则表达式的字符串或者一个串的模式,flags为一个编译标志位,用于修改匹配方式,如:是否区分大小写,多行匹配等,常用flags如下:
标志 | 说明 |
---|---|
re.A或ASCII | 对于\w、\W、\b、\B、\d、\D、\s和\S只进行ASCII(仅Python3) |
re.I或IGNORECASE | 执行不区分字母大小写的匹配 |
re.M或MULTILINE | 将^和$用于整个字符串的开始和结尾的每一行(默认情况下仅适用于整个字符串的开始和结尾处) |
re.S或DOTALL | 使用. 字符匹配所有字符,包括换行符 |
re.X或VERBOSE | 忽略模式字符串中未转义的空格和注释 |
- re.match(pattern,string[,flags]):匹配字符串,
返回一个Match对象
,他的方法有:Match.start()返回匹配值的起始位置,Match.end()返回匹配值的结束位置,Match.span()返回匹配位置的元组,Match.string为要匹配的字符串,Match.group()为所匹配成功的串; - re.search(pattern,string[,flags]):搜索第一个匹配的值,匹配成功返回Match对象,否则返回None`;
import re
pattern=r'\w*shop'
pattern2=r'.*?shop'
string='mr______________shop mr_shop'
print(re.search(pattern,string))
print(re.search(pattern2,string))
# -><re.Match object; span=(0, 20), match='mr______________shop'>
# -><re.Match object; span=(0, 28), match='mr______________shop mr_shop'>
- re.findall(pattern,string[,flags]):findall()方法用来寻找所有的符合正则表达式的字符串,并以列表的形式返回,即
返回值为一个包含查找结果字符串的列表
;
import re
pattern=r'\w*shop'
pattern2=r'.*?shop'
pattern3=r'.*shop'
string='mr______________shop mr_shop'
print(re.findall(pattern,string))
print(re.findall(pattern2,string))
print(re.findall(pattern3,string))
# ->['mr______________shop', 'mr_shop']
# ->['mr______________shop', ' mr_shop']
# ->['mr______________shop mr_shop']
从上面的结果也可以发现,findall()函数对于贪婪模匹配与懒惰匹配的结果的不同之处;
需要注意的是,如果在指定的模式字符串中包含元组,则返回与分组匹配的文本列表,所以如果想要获取整个模式字符串的匹配,可以将整个模式字符串分成一个组,然后在获取返回值列表的每个元素(是一个元组)的第一个元素;代码如下:
import re
pattern1=r'[1-9]{1,3}(\.[0-9]{1,3}){3}'
pattern2=r'([1-9]{1,3}(\.[0-9]{1,3}){3})'
string='127.0.0.1 192.168.1.66'
print(re.findall(pattern1,string))
print(re.findall(pattern2,string))
print(re.findall(pattern2,string)[0][0])
print(re.findall(pattern2,string)[1][0])
# ->['.1', '.66']
# ->[('127.0.0.1', '.1'), ('192.168.1.66', '.66')]
# ->127.0.0.1
# ->192.168.1.66
- re.sub(pattern,repl,string,count,flags):该方法用于字符串替换,pattern为模式字符串,由正则表达式转换而来,repl为需要替换的字符串,string需要被查找替换的原始字符串,count为可选参数表示替换次数,flags为可选参数,为标志位,用于控制匹配方式;
它的返回值为替换后的串
import re
pattern=r'shop'
string='mr_____shop mr_shop'
print(re.sub(pattern,'market',string))
print(re.sub(pattern,'market',string,1))
print(string)
# ->mr_____market mr_market
# ->mr_____market mr_shop
# ->mr_____shop mr_shop
- re.split(pattern,string,[maxsplit],[flags]):maxsplit为分割次数,
返回值为切割后的列表
;
import re
pattern=r'shop'
string='mr_____shop mr_shop mr__shop'
print(re.split(pattern,string))
print(re.split(pattern,string,2))
# ->['mr_____', ' mr_', ' mr__', '']
# ->['mr_____', ' mr_', ' mr__shop']