一、什么是正则表达式
在介绍正则表达式前,我们需要树立一个概念:所有内容本质上都是一个字符,比如我们书写的文字就是一连串字符组成的字符串;比如字母,数字,标点符号和其他符号等等。
正则表达式(Regular Expression,常简写为regex、regexp或RE),是一组由字母和符号组成的特殊文文本,所有内容本质都是一个字符,在日常的编程中,经常使用正则表达来来检索、替换、提取文档中的内容。
比如我们需要搜索Hello World!
中的字符时,只需要一个正则表达式Hello
,即可完成匹配。
"Hello" => Hello World!.
正则表达式适用于不同编程语言,规则还是重点,无论是C++,Java还是Python的编程使用正则表达式的规则基本一致,只是调用不同方法,因此学会了正则表达式的规则就可以在不同编程语言使用,本文为了让读者更加直观的理解规则,使用Python作为示例。
二、元字符
正则表达式主要由元字符组成。 这些元字符不再表示它们原本的意思,而是有特殊的含义。下面是常用的元字符在正则表达式中的含义:
三、如何使用正则表达式
在Python中使用正则表达式之前,需要先使用import re
导入正则表达式库。在日常使用中,主要用到re.search()
和re.findall()
两个方法来检索和提取文本内容,后续的内容将会结合这两个方法和正则表达式的规则进行分享。
如果您使用其他的编程语言,也可以找到对应的方法。
import re
3.1 基本匹配
方法re.search()
用于查找字符串中和正则表达式相匹配的内容,如果存在相匹配的内容,返回一个Match
对象;未找到匹配项,则返回None
。
在日常文本提取和爬虫中,常用于判断语句中,如果检索的字符串存在相应的内容,再进行进一步的操作,例如,判断字符串Hello, World
中是否含有Hello
,如果存在则输出原来的字符串。
string = 'Hello, World'if re.search('Hello', string):print(string)else:print('Cannot find!')Hello, World
3.2 数字提取
字符包括字母,也包括数字。数字0-9也是字符,可以使用字符d
匹配0到9之间的任何数字。+字符
d
表示它是一个元字符。
string = '123abc'string_2 = '12abc'if re.search('ddd', string): # string中包含123三个数字可以匹配print(string)if re.search('ddd', string_2): # string中仅包含12两个数字无法匹配print(string_2)123abc
3.3 任意字符
在某些纸牌游戏中,大小王可以代表任何纸牌。在正则表达式中,元字符.
功能类似。可以匹配任何单个字符(字母,数字,空格等等)。
.
常用于知道具有相同的模式或结构但不确定内容的文本,例如:电话号码、邮政编码等等。
string = '0755-123'string_2 = '0755-abc'if re.search('....-...', string):print(string)if re.search('....-...', string_2):print(string_2)0755-1230755-abc
3.4 特定字符
上一节中元字符.
功能非常强大,但有时功能太强大;例如,上述的字符串0737-abc
包含字母也被匹配上了,事实上电话号码中并不含有字母。
正则表达式中方括号[]
表示匹配特定字符,例如,[123]
仅匹配1,2,3三个数字中的一个,而不匹配其他项。
string = '0755-123'string_2 = '0737-abc'if re.search('....-[123][123][123]', string):print(string)if re.search('....-[123][123][123]', string_2):print(string_2)0755-123
3.5 字符范围
除了匹配或排除特定字符之外,如果我们要匹配连续范围内的字符的字符时,可以通过使用-
破折号来指示字符范围,例如,[0-6]
将只匹配从0到6的任何一位数字,而没有其他字符。同样,[^n-p]
将仅匹配除n至p之外任何单个字符。
一个方括号内也可以使用多个字符范围,例如,[A-Za-z0-9_]
,通常用于匹配英文文本中的字符。
string = 'abc-123'string_2 = 'xyz-124'if re.search('...-[1-3][1-3][1-3]', string): print(string)if re.search('...-[1-3][1-3][1-3]', string_2):print(string_2)abc-123
3.6 重复
ddd
会精确匹配三位数字,但是如果匹配20个数字,这些的写法就会变得不方便并且影响程序的美观,一种更方便的方法是使用{}
大括号指定每个字符要重复多少次。例如,d{20}
就可以完成20个数字的匹配.
此外,正则表达式可以指定此重复次数的范围,例如,a{1,3}
表示匹配不超过3次,但不少于一次。
大括号{}
可以与任何字符或者元字符一起使用,例如,[wxy]{5}
表示重复五个字符,每个字符可以是w,x或y。
string = 'aaaaaaaa-111'string_2 = 'xyzzxzy-124'if re.search('[a]{8}-d{3}', string):print(string)if re.search('[xyz]{6}-', string_2):print(string_2)aaaaaaaa-111xyzzxzy-124
3.7 至少
元字符*
或者+
以匹配任意数量的字符,例如:使用d*
可以匹配任意个数的数字,d+
匹配至少包含一位数字。
在正则表达式中,我们可以将任何字符或特殊元字符一起使用,组成更加复杂的内容匹配,例如,[abc]+
匹配a,b或c任何字符中的一个或多个。
string = 'aaaaaaaa-111'string_2 = 'xyzzxzy-124'if re.search('[a]+-d{3}', string):print(string)if re.search('[xyz]*-', string_2):print(string_2)aaaaaaaa-111xyzzxzy-124
3.8 可选字符
?
元字符表示可选,用于匹配文本中不确定的字符,例如,ab?c
将匹配字符串“abc”或“ac”,b被认为是不确定的内容.
string = 'abc-123'string_2 = 'ac-124'if re.search('ab?c', string): print(string)if re.search('ab?c', string_2):print(string_2)abc-123ac-124
3.9 空白
在实际处理文本时,经常遇到空格。最常见的空格形式是空格(␣)
,制表符(t)
,换行符(n)
和回车符(r)
等,s
将任何特定空格匹配,在处理原始输入文本时非常有用。
string = ' abc-123'string_2 = 'ac124'if re.search('s', string):print(string)if re.search('s', string_2):print(string_2)abc-123
3.10 开始和结束
^
和$
元字符定义一个行的开始和结束;例如,我们可以使用^abc
来仅匹配以“abc”开头的行,可以使用123$
来匹配以“123”结束的行。
string = 'abc-123'string_2 = 'ac124'if re.search('^abc', string): print(string)if re.search('^abc', string_2):print(string_2)abc-123string = 'abc-123'string_2 = 'ac124'if re.search('123$', string):print(string)if re.search('^123$', string_2):print(string_2)abc-123
3.11 逻辑运算符
在正则表达式中,可以使用|
表示逻辑“或”运算,匹配可能的字符。例如,(abc|xyz)
可以匹配abc或xyz,
string = 'abc-xyz'string_2 = 'xyz-abc'if re.search('^abc|xyz', string): print(string)if re.search('^abc|xyz', string_2):print(string_2)abc-xyzxyz-abc
3.12 嵌套
面对复杂数据时,我们也许需要提取多层信息,()
可以用于提取嵌套的信息,例如,1280x720中,我们学要提取1280和720这两个数字,而不要提取x
这个符号。
Python中re.findall()
方法,常用于文本的提取,第一个参数是对应的正则表达式,第二个参数是需要提取的文本内容。
string = '1280x720-abc-abc'string_2 = 'ac124-abc-'print(re.findall('(d{4})x(d{3})',string))print(re.findall('(abc)-',string_2))[('1280', '720')]['abc']
3.13 贪婪匹配
贪婪匹配是指正则表达式会尽可能长的匹配字符串,例如,用^F.+:
匹配字符串'From: Michael, Reason: the'
时,会匹配到'From: Michael, Reason:'
,而非'From:'
。
正则表达式默认采用贪婪匹配,我们可以使用?
将贪婪匹配转化为非贪婪匹配匹配;将上述^F.+:
修改为^F.+?:
就可以只匹配到'From:'
。
string = 'From: Michael, Reason: the'print(re.findall('^F.+:',string))print(re.findall('^F.+?:',string))['From: Michael, Reason:']['From:']
常用匹配
下面是经常使用的匹配规则,无需逐一掌握,在需要使用正则表达式时,可以对照此表,结合上述掌握的规则,基本可以实现日常需求。
数字匹配
1. 数字:^[0-9]*$2. n位的数字:^d{n}$3. 至少n位的数字:^d{n,}$4. m-n位的数字:^d{m,n}$5. 正数、负数、和小数:^(-|+)?d+(.d+)?$6. 小数:^(-?d+)(.d+)?$7. 正小数:^[1-9]d*.d*|0.d*[1-9]d*$ 8. 负小数:^-([1-9]d*.d*|0.d*[1-9]d*)$ 9. 非负整数:^d+$ 或 ^[1-9]d*|0$10. 非正整数:^-[1-9]d*|0$11. 非负小数:^d+(.d+)?$12. 非正小数:^((-d+(.d+)?)|(0+(.0+)?))$
字符串
1. 26个英文字母组成的字符串:^[A-Za-z]+$2. 26个大写英文字母组成的字符串:^[A-Z]+$3. 26个小写英文字母组成的字符串:^[a-z]+$4. 数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$5. 数字、26个英文字母或者下划线组成的字符串:^w+$ 或 ^w{3,20}$
特殊需求
1. Email地址:^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$2. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?3. URL:[a-zA-z]+://[^s]4. 手机号码:^(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])d{8}$ 5. 日期格式:^d{4}-d{1,2}-d{1,2}6. 12个月(01~09和1~12):^(0?[1-9]|1[0-2])$7. 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$ 8. IP地址:d+.d+.d+.d+