Python使用正则表达式

  • 正则表达式(Regular Expression),又称规则表达式,是一个计算机科学的概念,通常被用来检索和替换符合某些规则的文本。

1. 正则表达式语法

  • 正则表达式就是记录文本规则的代码。

1. 行定位符

  • 行定位符就是用来描述字符串的边界。“^”表示行的开始,“$”表示行的结尾。
^tm     # 表示匹配行头为“tm”的字符串。
# tm equal Tomorrow Moon匹配,Tomorrow Moon equal tm不匹配。
tm$     # 匹配以“tm”为行尾的字符串。
# tm equal Tomorrow Moon不匹配,Tomorrow Moon equal tm匹配。
tm     # 匹配任意位置出现了“tm”的字符串
# tm equal Tomorrow Moon匹配,Tomorrow Moon equal tm匹配。

2. 元字符

\bmr\w*\b     # 匹配以字母mr开头的单词,先是从某个单词开始处(\b),然后匹配字母mr,接着是任意数量的字母或数字(\w*),最后是单词结束处(\b)。
# 该表达式可以匹配“mrsoft”、“mrbook”、“mr123456”等。

常用元字符

代码 说明
. 匹配除换行符以外的任意字符。
\w 匹配字母或数字或下划线或汉字。
\s 匹配任意的空白符。
\d 匹配数字。
\b 匹配单词的开始或结束。
^ 匹配字符串的开始。
$ 匹配字符串的结束。

3. 重复

  • “\w*”用于匹配任意数量的字母或数字。如果想匹配特定数量的数字,如何表示呢?正则表达式为我们提供了限定符(指定数量的字符)来实现该功能。
^\d{
    
    8}$       # 匹配8位QQ号

常用限定符

限定符 说明 举例
? 匹配前面的字符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)$  # 该表达式的意思是可匹配15位数字,或者18位数字,或者17位数字和最后一位,最后一位可以是数字或者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   # 该表达式的含义是匹配单词thirth或fourth,如果不使用小括号,那么就变成了匹配单词thir和fourth了。
  • 小括号的第二个作用是分组,也就是子表达式。例如(.[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])
# 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:     # 判断是否为None,为真表示匹配失败
    print(mobile, '不是有效的中国移动手机号码。')
else:
    print(mobile, "是有效的中国移动手机号码。")
mobile = '13144222221'
match = re.match(pattern, mobile)    # 进行模式匹配
if match is None:     # 判断是否为None,为真表示匹配失败
    print(mobile, "不是有效的中国移动手机号码。")
else:
    print(mobile, "是有效的中国移动手机号码。")
  • 运行结果:
13634222222 是有效的中国移动手机号码。
13144222221 不是有效的中国移动手机号码。

2. 使用search()方法进行匹配

  • search()方法用于在整个字符串中搜索第一个匹配的值,如果在起始位置匹配成功,则返回Match对象,否则返回None,语法格式如下:
re.search(pattern, string, [flags])
# 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     # 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:     # 判断是否为None,为真表示匹配失败
    print(about, '@ 安全!')
else:
    print(about, '@ 出现了危险词汇!')
  • 运行结果如下:
我是一名程序员,我喜欢看黑客方面的图书,想研究一下Trojan。 @ 出现了危险词汇!
我是一名程序员,我喜欢看计算机网络方面的图书,喜欢开发网站。 @ 安全!

3. 使用findall()方法进行匹配

  • findall()方法用于在整个字符串中搜索所有符合正则表达式的字符串,并以列表的形式返回。如果匹配成功,则返回包含匹配结构的列表,否则返回空列表。
  • 语法如下:
re.findall(pattern, string, [flags])
# pattern:表示模式字符串,由要匹配的正则表达式转换而来。
# string:表示要匹配的字符串。
# flags:可选参数,表示标志位,用于控制匹配方式,如是否区分字母大小写。

示例一

  • 搜索以“mr_”开头的字符串:
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()方法

  • sub()方法用于实现字符串替换。语法格式如下:
re.sub(pattern,  repl, string, count, flags)
# pattern:表示模式字符串,由要匹配的正则表达式转换而来。
# repl:表示替换的字符串。
# string:表示要被查找替换的原始字符串。
# count:可选参数,表示模式匹配后替换的最大次数,默认值为0,表示替换所有的匹配。
# 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])
# pattern:表示模式字符串,由要匹配的正则表达式转换而来。
# string:表示要匹配的字符串。
# maxsplit:可选参数,表示最大的拆分次数。
# flags:可选参数,表示标志位,用于控制匹配方式,如是否区分大小写字母。

示例一

  • 从给定的URL地址中提取出请求地址和各个参数:
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)    # 输出每个好友名
  • 运行结果:
您@的好友有:
明日科技
扎克伯格
盖茨

猜你喜欢

转载自blog.csdn.net/ungoing/article/details/130780443
今日推荐