正则表达式(Regular Expression),又称规则表达式,是一个计算机科学的概念,通常被用来检索和替换符合某些规则的文本。
1. 正则表达式语法
1. 行定位符
行定位符就是用来描述字符串的边界。“^”表示行的开始,“$”表示行的结尾。
^ tm
tm$
tm
2. 元字符
\bmr\w* \b
常用元字符
代码 说明 . 匹配除换行符以外的任意字符。 \w 匹配字母或数字或下划线或汉字。 \s 匹配任意的空白符。 \d 匹配数字。 \b 匹配单词的开始或结束。 ^ 匹配字符串的开始。 $ 匹配字符串的结束。
3. 重复
“\w*”用于匹配任意数量的字母或数字。如果想匹配特定数量的数字,如何表示呢?正则表达式为我们提供了限定符(指定数量的字符)来实现该功能。
^ \d{ 8 } $
常用限定符
限定符 说明 举例 ? 匹配前面的字符0次或1次。 colou?r,该表达式可以匹配colour和color。 + 匹配前面的字符1次或多次。 go+gle,该表达式可以匹配的范围从gogle到goo…gle。 * 匹配前面的字符0次或多次。 go*gle,该表达式可以匹配的范围从ggle到goo…gle。 {n} 匹配前面的字符最少n次。 go{2}gle,该表达式只匹配google。 {n,} 匹配前面的字符最少n次。 go{2,}gle,该表达式可以匹配的范围从google到goo…gle。 {n,m} 匹配前面的字符最少n次,最多m次。 employe{0, 2},该表达式可以匹配employ、employe和employee共3种情况。
4. 字符类
正则表达式查找数字或字母是很简单的,因为已经有了这些字符集合的元字符(如\d、\w),但是如果要匹配没有预定义元字符的字符集合(比如元音字母a, e, i, o,u),应该怎么办? 很简单,只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号“.”、“?”或“!”。也可以轻松地指定一个字符范围,像[0-9]代表的含义与\d就是完全一致的:代表一位数字;同理,[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。 想要匹配给定字符串中任意一个汉字,可以使用[\u4e00-\u9fa5];如果要匹配连续多个汉字,可以使用[\u4e00-\u9fa5]+。
5. 排除字符
上一节是匹配符合指定字符集合的字符串。现在反过来,匹配不符合指定字符集合的字符串。正则表达式提供了“^”字符。这个元字符在前面出现过,表示行的开始,在这里将会放到方括号中,表示排除的意思。
[ ^ a- zA- Z]
6. 选择字符
试想一下,如何匹配身份证号码呢?首先需要了解一下身份证号码的规则。身份证号码长度为15位或18位。如果是15位,则全为数字;如果是18位,前17位是数字,最后一位是校验位,可能是数字或字符X。 上面描述中,包含着条件选择的逻辑,这就需要使用选择字符(|)来实现。该字符可以理解为“或”,匹配身份证的表达式可以写成如下:
( ^ \d{ 15 } $) | ( ^ \d{ 18 } $) | ( ^ \d{ 17 } ) ( \d| X| x) $
7. 转义字符
正则表达式中的转义字符(\)和Python中的大同小异,都是将特殊字符(如“.”、“?”、“\”等)变为普通的字符。举一个IP地址的实例,用正则表达式匹配如127.0.0.1这样格式的IP地址。如果直接使用点字符,格式为:
[ 1 - 9 ] { 1 , 3 } . [ 0 - 9 ] { 1 , 3 } . [ 0 - 9 ] { 1 , 3 } . [ 0 - 9 ] { 1 , 3 }
上面这种显然是不对的,因为“.”可以匹配一个任意字符。这时,不仅是127.0.0.1这样的IP,连127101011这样的字符串也会被匹配出来。所以在使用“.”时,需要使用转义字符(\)。修改后的正则表达式如下:
[ 1 - 9 ] { 1 , 3 } \. [ 0 - 9 ] { 1 , 3 } \. [ 0 - 9 ] { 1 , 3 } \. [ 0 - 9 ] { 1 , 3 }
8. 分组
通过上面第6个例子,我们已经对小括号的作用有一定的了解了。小括号字符的第一个作用就是可以改变限定符的作用范围 ,如“|”、“*”、“^”等。
( thir| four) th
小括号的第二个作用是分组 ,也就是子表达式。例如(.[0-9]{1,3}){3},就是对分组([0-9]{1,3})进行重复操作。
9. 在Python中使用正则表达式语法
在Python中使用正则表达式时,是将其作为模式字符串 使用的。例如,将匹配不是字母的一个字符的正则表达式表示为模式字符串,可以使用下面的代码:
'[^a-zA-Z]'
如果将匹配以字母m开头的单词的正则表达式转换为模式字符串,则不能直接在其两侧添加引号定界符,例如,下面的代码是不正确的:
'\bm\w*\b'
'\\bm\\w*\\b'
由于模式字符串可能包括大量的特殊字符和反斜杠,所以需要写为原生字符串,即在模式字符串前加r或R。例如,上面的模式字符串采用原生字符串表示就是:
r'\bm\w*\b'
在编写模式字符串时,并不是所有的反斜杠都需要进行转换。例如,前面编写到的正则表达式“^\d{8}$”中的反斜杠就不需要转义,因为其中的\d并没有特殊的意义。不过,为了编写方便,自己所写的正则表达式建议都采用原生字符串表示。
2. 使用re模块实现正则表达式操作
前面介绍了正则表达式的语法,下面将介绍如何在Python中使用正则表达式。Python提供了re模块,用于实现正则表达式的操作。在实现时,可以使用re模块提供的方法(如search()、match()、findall()等)进行字符串的处理,也可以先使用re模块的compile()方法将模式字符串转换为正则表达式对象,然后再使用该正则表达式对象的相关方法来操作字符串。 re模块在使用时,需要用import语句引入:
import re
如果使用的时候没有引入,会抛出模块未定义的异常:
1. 匹配字符串
匹配字符串可以使用re模块提供的match()、search()、findall()等方法。
1. 使用match()方法进行匹配
match()方法用于从字符串的开始处进行匹配,如果在其实位置匹配成功,则返回Match对象,否则返回None。语法如下:
re. match( pattern, string, [ flags] )
标志 说明 A或ASCII 对于\w、\W、\b、\B、\d、\D、\s和\S只进行ASCII匹配(仅适用于Python3.x)。 I或IGNORECASE 执行不区分字母大小写的匹配。 M或MULTILINE 将^和$用于包括整个字符串的开始和结尾的每一行(默认情况下,仅适用于整个字符串的开始和结尾处)。 S或DOTALL 使用“.”字符串匹配所有字符,包括换行符。 X或VERBOSE 忽略模式字符串中未转义的空格和注释。
示例一
例如匹配字符串是否以“mr_”开头,不区分字母大小写:
import re
pattern = r'mr_\w+'
string = 'MR_SHOP mr_shop'
match = re. match( pattern, string, re. I)
print ( match)
string = "项目名称 MR_SHOP mr_shop"
match = re. match( pattern, string, re. I)
print ( match)
< re. Match object ; span= ( 0 , 7 ) , match= 'MR_SHOP' >
None
字符串“MR_SHOP”是以“mr_”开头,所以返回一个Match对象,而字符串“项目名称MR_SHOP”不是以“mr_”开头,所以返回None。这是因为match()方法从字符串的开始位置开始匹配 ,当第一个字母不符合条件时,则不再进行匹配,直接返回None。 Match对象中包含了匹配值的位置和匹配数据。其中,要获取匹配值的起始位置 可以使用Match对象的start()方法 ;要获取匹配值的结束位置 可以使用end()方法 ;通过span()方法 可以返回匹配位置的元组 ;通过string属性 可以获取要匹配的字符串 。
import re
pattern = r'mr_\w+'
string = 'MR_SHOP mr_shop'
match = re. match( pattern, string, re. I)
print ( '匹配值的起始位置:' , match. start( ) )
print ( '匹配值的结束位置:' , match. end( ) )
print ( '匹配位置的元组:' , match. span( ) )
print ( '要匹配的字符串' , match. string)
print ( '匹配数据:' , match. group( ) )
匹配值的起始位置: 0
匹配值的结束位置: 7
匹配位置的元组: ( 0 , 7 )
要匹配的字符串 MR_SHOP mr_shop
匹配数据: MR_SHOP
示例二
import re
pattern = r'(13[4-9]\d{8})$|(15[01289]\d{8})$'
mobile = '13634222222'
match = re. match( pattern, mobile)
if match is None :
print ( mobile, '不是有效的中国移动手机号码。' )
else :
print ( mobile, "是有效的中国移动手机号码。" )
mobile = '13144222221'
match = re. match( pattern, mobile)
if match is None :
print ( mobile, "不是有效的中国移动手机号码。" )
else :
print ( mobile, "是有效的中国移动手机号码。" )
13634222222 是有效的中国移动手机号码。
13144222221 不是有效的中国移动手机号码。
2. 使用search()方法进行匹配
search()方法用于在整个字符串中搜索第一个匹配的值,如果在起始位置匹配成功,则返回Match对象,否则返回None,语法格式如下:
re. search( pattern, string, [ flags] )
示例一
搜索第一个以“mr_”开头的字符串,不区分字母大小写。
import re
pattern = r'mr_\w+'
string = 'MR_SHOP mr_shop'
match = re. search( pattern, string, re. I)
print ( match)
string = '项目名称 MR_SHOP mr_shop'
match = re. search( pattern, string, re. I)
print ( match)
< re. Match object ; span= ( 0 , 7 ) , match= 'MR_SHOP' >
< re. Match object ; span= ( 5 , 12 ) , match= 'MR_SHOP' >
从上面的运行结果可以看出,search()方法不仅是在字符串的起始位置搜索,其他位置有符合的匹配也可以。
示例二
import re
pattern = r'(黑客)|(抓包)|(监听)|(Trojan)'
about = '我是一名程序员,我喜欢看黑客方面的图书,想研究一下Trojan。'
match = re. search( pattern, about)
if match is None :
print ( about, '@ 安全!' )
else :
print ( about, '@ 出现了危险词汇!' )
about = '我是一名程序员,我喜欢看计算机网络方面的图书,喜欢开发网站。'
match = re. match( pattern, about)
if match is None :
print ( about, '@ 安全!' )
else :
print ( about, '@ 出现了危险词汇!' )
我是一名程序员,我喜欢看黑客方面的图书,想研究一下Trojan。 @ 出现了危险词汇!
我是一名程序员,我喜欢看计算机网络方面的图书,喜欢开发网站。 @ 安全!
3. 使用findall()方法进行匹配
findall()方法用于在整个字符串中搜索所有符合正则表达式的字符串,并以列表的形式返回。如果匹配成功,则返回包含匹配结构的列表,否则返回空列表。 语法如下:
re. findall( pattern, string, [ flags] )
示例一
import re
pattern = r'mr_\w+'
string = 'MR_SHOP mr_shop'
match = re. findall( pattern, string, re. I)
print ( match)
string = '项目名称 MR_SHOP mr_shop'
match = re. findall( pattern, string)
print ( match)
[ 'MR_SHOP' , 'mr_shop' ]
[ 'mr_shop' ]
示例二
如果在指定的模式字符传中包含分组,则返回与分组匹配的文本列表。
import re
pattern = r'[1-9]{1,3}(\.[0-9]{1,3}){3}'
str1 = '127.0.0.1 192.168.1.66'
match = re. findall( pattern, str1)
print ( match)
[ '.1' , '.66' ]
从上面的结果可以看出,并没有得到匹配的IP地址,这是因为在模式字符串中出现了分组,所以得到的结果是根据分组进行匹配的结果,即“(.[0-9]{1,3})”匹配的结果。如果想获取整个模式字符串的匹配,可以将整个模式字符串使用一对小括号进行分组。然后在获取结果时,只取返回值列表的每个元素(是一个元组)的第1个元素。
import re
pattern = r'([1-9]{1,3}(\.[0-9]{1,3}){3})'
str1 = '127.0.0.1 192.168.1.66'
match = re. findall( pattern, str1)
for item in match:
print ( item[ 0 ] )
127.0 .0 .1
192.168 .1 .66
2. 替换字符串——sub()方法
re. sub( pattern, repl, string, count, flags)
示例一
import re
pattern = r'1[34578]\d{9}'
string = '中奖号码为:84978981 电话为:13611111111'
result = re. sub( pattern, '1XXXXXXXXXX' , string)
print ( result)
中奖号码为:84978981 电话为:1XXXXXXXXXX
示例二
import re
pattern = r'(黑客)|(抓包)|(监听)|(Trojan)'
about = "我是一名程序员,我喜欢看黑客方面的图书,想研究一下Trojan。"
sub = re. sub( pattern, '@_@' , about)
print ( sub)
about = '我是一名程序员,我喜欢看计算机网络方面的图书,喜欢开发网站。'
sub = re. sub( pattern, '@_@' , about)
print ( sub)
我是一名程序员,我喜欢看@_@方面的图书,想研究一下@_@。
我是一名程序员,我喜欢看计算机网络方面的图书,喜欢开发网站。
3. 使用正则表达式分割字符串——split()方法
split()方法用于实现根据正则表达式分割字符串,并以列表的形式返回。其作用与字符串对象的split()方法类似,所不同的就是分割字符串由模式字符串指定。语法格式如下:
r. split( pattern, string, [ maxsplit] , [ flags] )
示例一
import re
pattern = r'[?|&]'
url = 'http://www.mingrisoft.com/login.jsp?username="mr"&pwd="mrsoft"'
result = re. split( pattern, url)
print ( result)
[ 'http://www.mingrisoft.com/login.jsp' , 'username="mr"' , 'pwd="mrsoft"' ]
场景模拟:微博的@好友栏目中,输入“@明日科技 @扎克伯格 @盖茨”(好友名称之间用一个空格区分),即可以同时@三个好友。
示例二
import re
str1 = '@明日科技 @扎克伯格 @盖茨'
pattern = r'\s*@'
list1 = re. split( pattern, str1)
print ( '您@的好友有:' )
for item in list1:
if item != '' :
print ( item)
您@的好友有:
明日科技
扎克伯格
盖茨