正则表达式
概述
正则表达式,regular Expression 缩写为 regex, regexp ,RE等
正则表达式是处理文本极为重要的技术,可以用它实现规则进行探索和替换.
1970年unix之父ken thompson将正则表达式引入到unix中文本编辑器ed和grep命令中由此正则表达式普及开来.
学习正则表达式w3网站
学习正则表达式菜鸟入口
正则表达式 – 教程 | 菜鸟教程 (runoob.com)
分类
-
BRE
基本正则表达式,grep.sed,vi等软件,vim具有扩展正则表达式.
-
ERE
拓展正则表达式,egrep(grep -e) ,sed -r等.
-
PCRE
几乎所有的高级语言都是PCRE的方言或者变种,python从1.6开始使用SRE生则表达式引擎,可以为是PCRE的子集,见模块re.
基本语法
元字符
metacharacter
代码 | 说明 | 举例 |
---|---|---|
. | 匹配除了换行符之外的所有字符 | . |
[abc] | 字符集合,只能表示一个字符位置,匹配所包含的任意字符 | [abc]匹配plain中的‘a’ |
[^abc] | 字符集合,只能表示一个字符位置,匹配除去集合内字符的任意字符 | [^abc]匹配plin中任何一个字符,不能匹配a,b,c |
[a-z] | 字符集合,只能表示一个字符位置,匹配集合内字符任意一个字符 | 经常使用[A-Z],[0-9] |
[^a-z] | 字符范围,也是一个集合,表示匹配除去集合内字符的任意字符. | |
\b | 匹配单词边界 | \bb表示在文本中找到b开头的单词b字符. |
\B | 不匹配单词边界 | t\B,包含t但是不以t结尾的单词比如write \Bb 不以b开头且含有b的单词比如abcd |
\d | 匹配[0-9]之间的数字,只匹配1个 | \d |
\D | \D类似 [ ^ 0 - 9 ],匹配一个非包含数字 | |
\s | 匹配一个空白字符,包括换行符,制表符,空格 [ \f \r \n \t \v ] | |
\S | 匹配一个非空白字符 | |
\w | 匹配[a-zA-Z0-9],包括文字 | \w |
\W | 匹配 \W之外的字符 | |
转义符
凡是在正则表达式中有特殊意义的符号,如果想要使用它的本意则需要转义
如果是反斜杠本身则需要 \\
\r \n 还是转义后代表回车和换行
重复
代码 | 说明 | 举例 |
---|---|---|
* | 表示前边正则表达式重复一次或者多次 | e\w* 单词e后方可以有多个非空白字符 |
+ | 表示前边正则表达式重复一次以上 | e\w+ 单词e后方至少有一个非空白字符 |
? | 表示前边正则表达式重复0此或者一次 | e\w? 单词e后方有一个或者0个非空白字符 |
{n} | 重复固定次数 | e\w{1},单词e后方只能有一个非空白字符 |
{n,} | 重复至少次数 | e\w{1}==e\w+ e\w{0}= e\w* e\w{0,1} = e\w? |
{n,m} | 重复n到m次 | e\w{1,10}e后方的空白字符至少1次最多10次 |
练习匹配电话号码 13位
\d{11}
匹配中国座机规律 前2-3位数字(-隔离)后七位数字
\d{3,4}-\d{7}
或
代码 | 说明 | 举例 |
---|---|---|
x|y | 匹配x或者y | wood took foot food 使用w|wood 或者 (w|f) ood |
捕获
代码 | 说明 | 举例 |
---|---|---|
(pattern) | 使用一个小括号可以代表一个子表达式,也可以叫做分组 捕获后会自动分配组好从1开始可以改变优先级 | |
\数字 | 匹配对应的分组 | (very)\1匹配very very,但是捕获的组group是very |
(?:pattern) | 如果仅仅为了改变优先级,就不需要捕获分组了 | (?:w|f)ood ‘industr(?:y|ise)’ 等价 industry|industrise |
(?exp) (?’name’exp) | 命名分组捕获,但是通过name访问分组 python的语法必须是(?’’exp) |
断言
零宽断言
测试字符串 wood took foot food
代码 | 说明 | 举例 |
---|---|---|
(?=exp) | 零宽容度正预测先行断言, 断言exp一定在匹配的右侧出现, 也就是说断言后一定跟一个exp | f(?=oo)f后边一定有oo出现 |
(?<=exp) | 零宽容度正回顾后断言 断言exp一定出现在匹配的左边出现, 也就是说前边一定有个exp前缀 | (?<=f)ood、(?<=t)ook分别匹配 ood,ook,ook前一定有t出现 |
负向零宽断言
代码 | 说明 | 举例 |
---|---|---|
(?!exp) | 零宽容度负预测先行断言 断言exp一定不会出现在右侧 也就是说断言后侧一定不是exp | \d{3}(?!\d)匹配三位数字,断言3位 数字后边一定不能是数字 foo(?!d)foo后边一定不能是d |
(?<!exp) | 零宽容负回顾后发断言 断言exp一定不能出现在左侧也就是说 断言前边一定不能是exp | (?<!exp)ood ood的左边一定不能f |
(?#comment) | 注释 | f(?=oo)(?#这个后断言不能被捕获) |
注意:
断言会不会捕获?就是断言占不占分组号呢?
断言是不会占分组号的,断言如同条件只是要求匹配
分组和捕获是同一个意思.
使用正则表达式,能用简单表达就不需要使用复杂的.
贪婪和非贪婪
默认是贪婪模式,也就是说尽可能的匹配更长的字符串.
非贪婪模式很简单,再重复的符号后边加一个?,就尽可能匹配了.
代码 | 说明 | 举例 |
---|---|---|
*? | 匹配任意次,但尽可能少重复 | |
+? | 匹配至少一个,但尽可能少重复 | |
?? | 匹配1次或者0次,但尽可能少重复 | |
{n,}? | 匹配至少n次,但尽可能少重复 | |
{n,m} | 匹配至少n-m次,但尽可能少重复 |
very very happy #使用非贪婪模式 v*.y v.*?y 匹配
引擎选项
代码 | 说明 | python |
---|---|---|
ignoreCase | 匹配时忽略大小写 | re.l re.IGNORECASE |
Singleline | 单行模式,可以匹配所有字符 包括\n | re.s re.DOTALL |
Muitline | 多行模式^行首$行尾 | re.M re.MUITLINE |
ignorepatternWhitespace | 忽略表达式中的空白字符,如果是Yoon给空白字符用转义 #可以用来做注释 | re.X re.VERBOSE |
单行模式
. 可以匹配所有字符,包括换行符
^表示整个字符串的开头字符 $表示整个字符串末尾字符
多行模式:
. 可以匹配除了换行符之外的所有字符,多行不影响.点好
^表示行首,$表示行尾 这里表示每一行
默认模式:
默认匹配将字符串看作为一行,不能看作为多行,.号不能匹配换行符,^表示行首$表示行尾行首行尾表示整个字符串的行首行尾
默认模式和单行模式区别:
基本相同,但是单行模式.可以匹配任何字符包括换行符,^$表示字符串开头和结尾.
默认模式和多行模式的区别:
定义了行的概念,但不影响.号的行为,^$还是行首行尾.只不过多行模式可以识别换行符.
简单理解:
单行模式只影响.号行为,多行模式重新定义^$.
ps:
注意看不到换行符 \r\n.
举例:
very very happy
my primary key
上方可能就有\r\n
单行匹配y$ 多行匹配happy和key的y
$匹配结尾
正则习题
IP地址
匹配合法的IP地址
192.168.1.150
0.0.0.0
255.255.255.255
17.16.52.100
172.16.0.100
400.400.999.888
001.022.003.000
257.257.255.256
#答案
(?:(25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)
提取文件名
选出含有ftp的链接,且文件类型是gz或者xz的文件名
ftp://ftp.astron.com/pub/file/file-5.14.tar.gz
ftp://ftp.gmplib.org/pub/gmp-5.1.2/gmp-5.1.2.tar.xz
ftp://ftp.vim.org/pub/vim/unix/vim-7.3.tar.bz2
http://anduin.linuxfromscratch.org/sources/LFS/lfs-packages/conglomeration//iana-etc/iana-etc-2.30.tar.bz2
http://anduin.linuxfromscratch.org/sources/other/udev-lfs-205-1.tar.bz2
http://download.savannah.gnu.org/releases/libpipeline/libpipeline-1.2.4.tar.gz
http://download.savannah.gnu.org/releases/man-db/man-db-2.6.5.tar.xz
http://download.savannah.gnu.org/releases/sysvinit/sysvinit-2.88dsf.tar.bz2
http://ftp.altlinux.org/pub/people/legion/kbd/kbd-1.15.5.tar.gz
http://mirror.hust.edu.cn/gnu/autoconf/autoconf-2.69.tar.xz
http://mirror.hust.edu.cn/gnu/automake/automake-1.14.tar.xz
#答案
^ftp.*/.(?:gz|xz)
匹配邮箱地址
test@hot-mail.com
v-ip@magedu.com
web.manager@magedu.com.cn
super.user@google.com
a@w-a-com
#答案
\w.*@.*
严谨点就是
\w[\w.-]*@.*
匹配html标记
提取href中的链接url,提取文字“马哥教育”
<a href='http://www.magedu.com/index.html' target='_blank'>马哥教育</a>
#答案
.*>\w+<.*
严谨点
<a.*?>[^><].*
<a.*[^><].*
匹配URL
http://www.magedu.com/index.html
https://login.magedu.com
file:///ect/sysconfig/network
#答案
(\w+)://(\s*)
#严谨点
^(f|h)\w+://.*
匹配二代中国身份证ID
321105700101003
321105197001010030
11210020170101054X
17位数字+1位校验码组成
前6位地址码,8位出生年月,3位数字,1位校验位(0-9或X)
#答案
\w{17}(\w|x)
#严谨
python_正则表达式
python的re模块提供了正则表达式的处理能力
常量
常量 | 说明 |
---|---|
re.M re.MULTILINE | 多行模式 |
re.S re.DOTALL | 单行模式 |
re.l re.IGNORECASE | 忽略大小写 |
re.X re.VERBOSE | 忽略表达式中的空白字符 |
使用|位或运算开启多种选项
re.M|re.X
方法
编译
re.compile(pattern,flage=0)
设定flage,编译模式,返回正则表达式对象regex.
pattern即为正则表达式字符串,flage即为选项,正则表达式需要编译为了提高效率,编译结果回保存,下次同样使用pattern时就不用再次编译了.
单词匹配
re.match (pattern,string,flage=0)
regex.match(pattern[,pos[, endpos]])
match匹配从字符串头开始匹配,regex对象math方法可以重定义开始位置和解决位置,返回match对象.
re.search(pattern, string,flage=0)
regex.search(string[,pos[,endpos]])
从头开始搜索对象匹配第一个,regex对象search方法可以重设置开始位置和结束位置,返回match对象.
re.fullmatch(pattern,string,flage=0)
regex.fullmatch(string[,pos[,endpos]])
整个字符串和正则表达式匹配
import re
s = '''bottle\nbag\nbig\napple'''
for i,c in enumerate(s,1):
print (i-1,c,end='\n' if i%10 ==0 else '' )
print ('match' '-------------------------------')
result = re.match('b',s)
print (1,result)
result = re.match('a',s)
print (2,result)
result =re.match('^a',s,re.M)
print (3,result)
result =re.match('^a',s,re.S)
print (4,result)
regex = re.compile('a')
result = regex.match(s)
print (5,result)
result = regex.match(s,15)
print (6,result)
全文搜索
re.findall (pattern,string,flags=0)
regex.findall(string[,pos[,endpos]])
对整个字符串,从左至右匹配,返回匹配项的列表
re.finditer(pattern,string,flags=0)
regex.findall(string[,pos[,endpos]])
对整个字符串从左至右匹配,返回所有匹配项,返回迭代器
注意每次迭代都是match对象
import re
s = '''bottle\nbag\nbig\nable'''
for i,c in enumerate(s,1):
print ((i-1,c),end='\n' if i%10 ==0 else '' )
result = re.findall('b',s)
print (1,result,sep='\n')
regex =re.compile('^b.*')
result = regex.findall(s)
print (2,result)
regex = re.compile('^b.*',re.M)
result = regex.findall(s,7)
print (3,result)
regex = re.compile('^b.*',re.S)
result = regex.findall(s,0,6)
print (4,result)
regex = re.compile('^b.*',re.M)
result = regex.findall(s,7,10)
print (5,result)
分别猜测我匹配的什么???
匹配替换
re.sub (pattern,replacement,string,count=0,flasg=0)
regex.sub(replacement,string ,count=0)
使用pattern对字符串string进行匹配,对匹配项使用replacement进行替换.
replacement可以是string,bytes,function
re.subn(pattern,replacement,string,count=0,flasg=0)
regex.subn(replacement,string,count=0)
同一个sub返回一个元组(new_string,number_of_subs_made)
import re
s = '''bottle\nbag\nbig\nable'''
for i,c in enumerate(s,1):
print ((i-1,c),end='\n' if i%10 ==0 else '' )
regex = re.compile('b\wg')
result = regex.sub('xiexie',s) #匹配多次
print (1,result)
result = regex.sub('xiexie',s,1) #匹配一次
print (2,result)
regex = re.compile('\s+') #匹配一个或者多个空白字符,换行符,制表符
result = regex.subn('\t',s) #将空白字符,换行符,制表符替换为字符串'\t',并且显示个数
print (3,result)
需要多次练习
分组
使用小括号的pattern捕获的数据放到group中.
macth,search函数返回macth对象,findall返回字符串列表,finditer返回一个个match对象,如果pattern中使用了分组,如果有匹配的结果,会在match对象中.
如果pattern中使用了分组,如果有匹配的结果会在match中.
-
使用group(N)的方式返回对应分组,1到N是对应分组,0返回整个匹配的字符串,N不写缺省为0
-
如果使用了命名分组,可以使用group(‘name’)的方式取分组
-
也可以使用groups()返回所有分组
-
使用groupdict()返回所有命名的分组.
import gettext
import re
s = '''bottle\nbag\nbig\nable'''
for i,c in enumerate(s,1):
print ((i-1,c),end='\n' if i%10 ==0 else '' )
regex = re.compile('(b\w+)')
result = regex.match(s)
print (type(result))
print (1,'match',result.group(0),result.group(1),result[0],result.groups())
result = regex.search(s,15) #可以设置开始位置search
print (2,'search',result.groups())
regex = re.compile('(b\w+)\n(?P<name1>b\w+)\n(?P<name2>b\w+)')
result = regex.match(s)
print (type(result))
print (3,'match',result.group(3),result.group(2),result.group(1))
print (4,'match',result.group(0).encode())
print (5,'match',result.group('name1'),result.group('name2'))
print (6,result.groups())
print (7,result.groupdict())
print (8,result)
print ('$' * 30 )
result = regex.findall(s)
for i in result:
print (type(i),i)
regex = re.compile('(?P<head>b\w+)')
result = regex.finditer(s)
print ('*' * 30 )
for i in result:
print (type(i),i,i.group(),i.group('head'),i['head'],i[0])
字符串分割
字符串的分割函数split,太难用,不能指定多个字符进行分割
re.split(pattern,string,maxsplit=0,flage=0)
split分割字符串
import gettext
import re
s = '''bottle\nbag\nbig\nable'''
for i,c in enumerate(s,1):
print ((i-1,c),end='\n' if i%10 ==0 else '' )
a = """
os.path.abspath(path)
normpath(join(os.getcwd(), path)).
"""
# print (re.split())
print (re.split('[\.()\s,]+',a)) #只能单行切
总结:
一般来说练习本课题需要反复练习代码,不到100行代码需要各位反复练习
import gettext
import re
s = '''bottle\nbag\nbig\nable'''
for i,c in enumerate(s,1):
print ((i-1,c),end='\n' if i%10 ==0 else '' )
result = re.match('b',s)
print (1,result)
result = re.match('a',s)
print (2,result)
result =re.match('^a',s,re.M)
print (3,result)
result =re.match('^a',s,re.S)
print (4,result)
regex = re.compile('a')
result = regex.match(s)
print (5,result)
result = regex.match(s,15)
print (6,result)
result = re.findall('b',s)
print (1,result,sep='\n')
regex =re.compile('^b.*')
result = regex.findall(s)
print (2,result)
regex = re.compile('^b.*',re.M)
result = regex.findall(s,7)
print (3,result)
regex = re.compile('^b.*',re.S)
result = regex.findall(s,0,6)
print (4,result)
regex = re.compile('^b.*',re.M)
result = regex.findall(s,7,10)
print (5,result)
print ('*&^%$#@@!!' * 30 )
regex = re.compile('^b\w+',re.M)
result = regex.finditer(s)
print (6,result)
r = next(result)
print (7,type(r),r)
print (8,r.start(),r.end(),s[r.start():r.end()])
print (9,type(r),r)
print (10,r.start(),r.end(),s[r.start():r.end()])
regex = re.compile('b\wg')
result = regex.sub('xiexie',s) #匹配多次
print (1,result)
result = regex.sub('xiexie',s,1) #匹配一次
print (2,result)
regex = re.compile('\s+') #匹配一个或者多个空白字符,换行符,制表符
result = regex.subn('\t',s) #将空白字符,换行符,制表符替换为字符串'\t',并且显示个数
print (3,result)
regex = re.compile('(b\w+)')
result = regex.match(s)
print (type(result))
print (1,'match',result.group(0),result.group(1),result[0],result.groups())
result = regex.search(s,15) #可以设置开始位置search
print (2,'search',result.groups())
regex = re.compile('(b\w+)\n(?P<name1>b\w+)\n(?P<name2>b\w+)')
result = regex.match(s)
print (type(result))
print (3,'match',result.group(3),result.group(2),result.group(1))
print (4,'match',result.group(0).encode())
print (5,'match',result.group('name1'),result.group('name2'))
print (6,result.groups())
print (7,result.groupdict())
print (8,result)
print ('$' * 30 )
result = regex.findall(s)
for i in result:
print (type(i),i)
regex = re.compile('(?P<head>b\w+)')
result = regex.finditer(s)
print ('*' * 30 )
for i in result:
print (type(i),i,i.group(),i.group('head'),i['head'],i[0])
a = """
os.path.abspath(path)
normpath(join(os.getcwd(), path)).
"""
# print (re.split())
print (re.split('[\.()\s,]+',a))