[Python]-16-正则表达式基础

引言

正则表达式不是Python语法的一部分,它是独立的用于处理字符串的强大工具,在支持正则表达式的语言里,正则表达式是通用的,这篇文章介绍Python中的re模块,它提供了常用的正则表达式语法支持。

文章目录

0×1.正则表达式基本语法

正则表达式的基本语法对于支持它的编程语言都是通用的,下面是一些常用语法与说明:

字符匹配:

● . :(英文句点)匹配任意单个字符(换行符\n除外);
● \ :(斜杠)用于特殊符号的转义;
● \d :匹配单个数字;
● \D :匹配单个非数字;
● \w :匹配单个单词字符(大小写字母以及数字);
● \W :匹配单个非单词字符;
● \s :匹配单个空白字符(包括:空格,\n,\r,\t,\f,\v);
● \S :匹配单个非空白字符;

范围与数量匹配:

● [...] :(中括号)匹配中括号中的单个字符,或字符范围中的单个字符,例如[abc]为匹配abc三个字符中的任意一个,[a-z]为匹配全体小写字母中的任意一个;
● [^...] :在中括号匹配的基础上取反,即不匹配中括号中的单个字符或范围中的单个字符;
● * :(星号)匹配星号前一个字符0次或无限次,例如"ab*c"可以匹配"ac","abc","abbbbc";
● + :(加号)匹配加号前一个字符1次或无限次,与星号唯一的区别就是,至少要匹配一次,例如"ab+c"不能匹配"ac",但可以匹配"abc","abbbbc";
● ? :(问号)匹配问号前一个字符0次或1次,例如"ab?c"可以匹配"ac","abc",但不能匹配"abbbbc";
● {m} :匹配大括号前一个字符m次,例如"ab{3}c"可以匹配"abbbc"但不能匹配"abc","abbc";
● {m,n} :匹配大括号前一个字符m至n次 ,如果省略m则默认m为0,如果省略n则默认n为无穷大,如两者都省略,等同于"星号",例如"ab{2,4}c"可以匹配"abbc","abbbc","abbbbc",但不能匹配"abc";
● *? :使星号匹配变成非贪婪模式,大部分程序支持中正则表达式默认都是使用贪婪模式,非贪婪模式多用于字符串分组,将在本文第二部分演示;
● +? :使加号匹配变成非贪婪模式;
● ?? :使问号匹配变成非贪婪模式;
● {m,n} ? :使大括号匹配变成非贪婪模式;

边界、逻辑与分组:

● ^ :(上标)匹配字符串开头,例如"^ab"能够匹配"ab123",但不能匹配"bb123",凡是以"ab"开头的字符串都能匹配;
● $ :(dollar符)匹配字符串结尾,例如".*\.py$"能够匹配所有".py"结尾的字符串;
● ^...$ :全匹配,例如"^abcd$"只能匹配字符串"abcd";
● | :(shift+\打出来的竖线)先判断|左边是否匹配,如果匹配就不用去判断右边,如果不匹配再判断右边是否匹配,例如"^abc|.*\.py$",匹配以"abc"开头,或以".py"结尾的所有字符串,如 "abchello","hello.py"; ● () :(中括号)括号中的字符将被当做一个整体,也经常与|配合使用,例如"a(\d\d)b"表示a和b之间必须包含两个数字(如"a12b"),"a(123|456)b"表示a与b之间必须包含123或456;

了解了这些基础规则后,下面来看python如何使用这些正则表达式。

0×2.Python常用正则表达式实例

a.re模块匹配实例

Python内置的re模块提供了对正则表达式的支持,先来看几个简单的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					#导入re模块
					import re

					#re.match(r"正则表达式","要匹配的字符串"),在正则表达式前使用r前缀,可以避免\的转义,本例匹配所有a与b之间包含三个数字的字符串
					rex=re.match(r"a...b", "a123b")
					print(rex) #打印出一个SRE_Match对象

					#re.math进行判断时,如果匹配返回一个SRE_Match对象,如果不匹配,返回None
					if rex:
					    print("True")
					else:
					    print("False")
					
					#程序输出
					<_sre.SRE_Match object; span=(0, 5), match='a123b'>
					True

					#修改上程序中的re.match(r"a...b", "a123b")部分,去掉print(rex)语句,使用第一部分介绍的正则表达式,看看程序如何输出
					
					#1.匹配数字和非数字
					re.match(r"a\d\d\db", "a111b")
					True
					re.match(r"a\d\d\db", "a1b")
					False
					re.match(r"a\Db", "a2b")
					False

					#2.匹配单词和非单词
					re.match(r"a\w\w\we", "abcde")
					True
					re.match(r"a\w\w\we", "a12De")
					True
					re.match(r"a\w\W\we", "a1_De")
					True

					#3.中括号匹配
					re.match(r"a[bcd]e", "abce")
					False
					re.match(r"a[bcd]e", "abe")
					True
					re.match(r"a[^bcd]e", "abe")
					False
					re.match(r"a[^bcd]e", "axe")
					True

					#4.星号匹配
					re.match(r"ab*c", "abc")
					True
					re.match(r"ab*c", "ac")
					True

					#5.加号匹配
					re.match(r"ab+c", "ac")
					False
					re.match(r"ab+c", "abbbc")
					True

					#6.问号匹配
					re.match(r"ab?c", "ac")
					True
					re.match(r"ab?c", "abbc")
					False

					#剩下的大家自己按照上面的格式去测试即可
					

b.字符串切片

使用正则表达式对字符串切片,比使用字符串自带的split方法更加的灵活和强大,请看下面的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					import re
					#普通的字符串split
					print("a b c  d   e".split(" "))
					#使用正则表达式实现split,使用一个或多个空格完成切片处理
					print(re.split(r"\s+", "a b c  d   e"))
					#包含一个或多个空格、原点、分号、逗号的切片处理
					print(re.split(r"[\s\.\;\,]+", "a ,b ;;c ... d   e"))

					#程序输出,普通的字符串切片对多个连续空格的处理会产生空的字符串,而正则表达式则不会
					['a', 'b', 'c', '', 'd', '', '', 'e']
					['a', 'b', 'c', 'd', 'e']
					['a', 'b', 'c', 'd', 'e']
					

c.字符串分组

re模块的groups()方法能够将匹配的字符串进行分组处理,每个需要分组匹配的正则表达式被包含在一对圆括号中,请看下面的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					import re
					#首先匹配后面的字符串是否以三位数字开头,6到8位数字结尾,如果匹配可以使用groups和group取出正则表达式对应括号中的内容
					a=re.match(r"^(\d{3})-(\d{6,8})$","010-88888888")
					if a:
					    print(a.groups()) #取出分组后的元祖列表
					    print(a.group(0)) #取出匹配到的全部字符
					    print(a.group(1)) #第一个括号中匹配的内容
					    print(a.group(2)) #第二个括号中匹配的内容,如果有多个括号以此类推

					#程序输出
					('010', '88888888')
					010-88888888
					010
					88888888
					

再来看一个计算机时间匹配的例子:

					#!/usr/bin/env python3
					#coding=utf-8
					import re
					t="23:53:09"
					#下面是一个全匹配,从头到尾,必须满足第一个分号前,第一位可以是0或1,第二位可以是0~9,或者第一位是2,第二位是0~3,或者0~9,这是我们一天时间的整点数,后面分别是从0~59的数字匹配
					a=re.match(r'^([01][0-9]|2[0-3]|[0-9]):([0-5][0-9]|[0-9]):([0-5][0-9]|[0-9])$', t)
					if a:
					    print(a.groups())
					    print(a.group(0))
					    print(a.group(1))
					    print(a.group(2))
					    print(a.group(3))

					#程序输出
					('23', '53', '09')
					23:53:09
					23
					53
					09
					

d.编译正则表达式

在上面的每一次正则表达式的匹配使用中,Python解释器都会首先编译正则表达式,然后再用编译后的正则表达式去匹配字符串,在大量重复的匹配环境下,这样的效率十分低;

如果一个正则表达式并不需要修改,那么就应该提前预编译(compile)它,之后每次的匹配操作都不需要再次编译,直接使用预编译好的规则去匹配即可,这样可以提高程序执行的效率,下面是一个预编译的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					import re
					t1="23:53:09"
					t2="19:02:12"
					#将匹配时钟的正则表达式预编译
					re_c=re.compile(r'^([01][0-9]|2[0-3]|[0-9]):([0-5][0-9]|[0-9]):([0-5][0-9]|[0-9])$')

					#之后的每一次匹配,都不需要再编译
					a=re_c.match(t1)
					b=re_c.match(t2)
					print(a.groups())
					print(b.groups())

					#程序输出
					('23', '53', '09')
					('19', '02', '12')
					

e.非贪婪模式匹配

在大部分语言的支持中,正则表达式是使用贪婪模式的,也就意味着尽可能多的匹配字符串(一少部分语言内置的是非贪婪模式),用一个实例来解释什么是贪婪模式:

					#!/usr/bin/env python3
					#coding=utf-8
					import re
					#匹配a开头,紧接着0个或多个b,结尾为一个或者多个b再加一个单字符c,贪婪模式下,第一组会尽可能多的匹配b,也就意味着,第二组只能匹配到一个bc
					print(re.match(r"^(ab*)(b+c)$","abbbbbc").groups())

					#非贪婪模式下,星号最少是0个,那么就使用0个作为匹配规则
					print(re.match(r"^(ab*?)(b+c)$","abbbbbc").groups())

					#加号匹配一个或多个,非贪婪模式就是一个
					print(re.match(r"^(ab+)(b+c)$","abbbbbc").groups())
					print(re.match(r"^(ab+?)(b+c)$","abbbbbc").groups())

					#问号匹配0个或者一个,非贪婪模式就是0个
					print(re.match(r"^(ab?)(b+c)$","abbbbbc").groups())
					print(re.match(r"^(ab??)(b+c)$","abbbbbc").groups())

					#程序输出,从输出中可以看出,贪婪模式是尽可能的多匹配,非贪婪模式是尽可能的少匹配
					('abbbb', 'bc')
					('a', 'bbbbbc')
					('abbbb', 'bc')
					('ab', 'bbbbc')
					('ab', 'bbbbc')
					('a', 'bbbbbc')