字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在。比如判断一个字符串是否是合法的Email地址,虽然可以编程提取@
前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦,而且代码难以复用。
正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
所以我们判断一个字符串是否是合法的Email的方法是:
因为正则表达式也是用字符串表示的,所以,我们要首先了解如何用字符来描述字符。
基础
\d 可以匹配一个数字
\w 可以匹配一个字母或数字
\s 匹配任意的空白字符。所谓空白字符,包括空格、tab键(制表符)、回车符、换行符等等。
. 可以匹配匹配任意单个字符
* 表示任意个字符(0个或多个)
+ 表示至少一个字符
? 表示0个或1个字符
{n}表示n个字符
{n,m}表示n~m个字符
例子:
'00\d'可以匹配'007'
'\w\w\d'可以匹配'py3'
'py.'可以匹配'pyc'、'pyo'、'py!'
\d{3}\s+\d{3,8} :\d{3}表示匹配3个数字,\s可以匹配一个空格,所以\s+表示至少有一个空格
如果要匹配’010-12345’这样的号码呢?由于’-'是特殊字符,在正则表达式中,要用\转义,所以,正则是
\d{3}\-\d{3,8}
正则表达式内部:如果有特殊字符也是需要手动转义的
进阶
要做更精确地匹配,可以用[]
表示范围,[]内是所有可选项
a-b :表示从a~b的所有字符
[0-9a-zA-Z\_] 可以匹配一个数字、字母或者下划线
[0-9a-zA-Z\_]+ 可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z'
[a-zA-Z][0-9a-zA-Z\_]可以匹配由字母开头,后接任意个由一个数字、字母或者下划线组成的字符串
[a-zA-Z][0-9a-z]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)
A|B 表示:A或B,所以(P|p)ython可以匹配'Python'或者'python'。
^ 表示行的开头,^\d表示必须以数字开头。
$ 表示行的结束,\d$表示必须以数字结束。
^py$ 表示结果只能是py
在任意编程语言中,定义的一个用户的一个字符串,在被加载的时候不是不变的,比如s = “\n"会被解析成为一个换号,但是当我们需要输出”\n"时,我们要这样写s = "\\n"
,也就是在前面再加一个"\"
,表示后面的这个斜杠是一个普通斜杠,表示它不用被转义。简单点说,一个字符串默认有可能会被解析成一个特殊的含义(换行),但是我们不想要这样,所以需要自己去转义这个特殊字符,使得得到我们想要的结果。一般情况下,都是使用\
来转义字符。
也就是说,转义特殊字符需要手动在前面加\
,python中很方便
正则表达式
在写正则表达式时,对于一些特殊字符的匹配也是需要转义的。比如想要匹配一些特殊的字符,比如-,-在正则表达式中就是个特殊字符,用来表示一个范围,如果要匹配"1-1"这样的情况,正则表达式是"\d\-\d"
,而不是"\d-\d"
python
在python中可以直接使用re.match函数去匹配正则表达式
但是,py中定义了一个字符串,很有可能被python给转义成其他东西了,所以需要转义,py有两种方法
方法1:使用只读参数r,强制不转义
s = r'ABC\-001'
方法2:re.escape
转义特殊字符需要手动在前面加\
,python中有现成的api,可以将给定字符串中的所有特殊字符前面都加上\
,也就是re.escape()函数
pattern1 = re.escape("a*b") # 相当于pattern1 = a\*b
pattern2 = "a\*b" # pattern2和pattern1一样
例子
import re
# 想要匹配的表达式是'a\-',但是py把斜杠转义了,导致pattern从'a\\-'变成了'a\-',导致匹配到'a-'也成功了
pattern = 'a\\-'
input = 'a-'
res = re.match(pattern, input) # 匹配成功
print(res)
# 正确写法
pattern = r'a\\-' # 只读,不解析
input = 'a\-'
res = re.match(pattern, input) # 成功
print(res)
pattern = re.escape('a\\-') # 调用api自动加\
input = 'a\-'
res = re.match(pattern, input) # 成功
print(res)
match()方法判断是否匹配,如果匹配成功,返回一个
Match对象,否则返回
None
例子
import re
pattern = r".a."
url = "Aa9"
if re.match(pattern, url):
print("匹配成功!")
else:
print("匹配失败!")
# 输出: 匹配成功
# 匹配邮箱
pattern = r'^[a-zA-Z0-9\.]+@[a-zA-Z0-9\.]+\.com$'
pattern = r'^[\w\.]+@[\w\.]+\.com$' # 简写
print(re.match(pattern,'[email protected]'))
print(re.match(pattern,'[email protected]'))
扩展
可以提取出带名字的Email地址:
[email protected] => Tom Paris
[email protected] => bob
import re
def name_of_email(addr):
re_name_of_email = re.compile(r'([a-zA-Z\s]+)?>?\s?([0-9a-zA-Z]+)*@[0-9a-zA-Z]+.org')
m = re_name_of_email.match(addr)
print(m.groups())
if m.group(1):
return m.group(1).strip()
else:
return m.group(2)
# 测试:
assert name_of_email(' [email protected]') == 'Tom Paris'
assert name_of_email('[email protected]') == 'tom'print('ok')
reference:https://www.python100.com/html/79913.html
在Python的正则表达式(re)模块中,re.escape函数是一个非常有用的工具,它用于将一个字符串中的特殊字符转义成正则表达式中的普通字符。在使用正则表达式进行字符串的匹配时,如果字符串中包含了正则表达式的特殊字符,那么就需要使用re.escape函数来对这些特殊字符进行转义,让它们变成普通字符,以保证匹配的准确性。
简单点说,re.escape函数就是将字符串中的所有特殊字符前面全部加上\
语法如下
re.escape(string)
其中,string是需要进行转义的字符串参数。
例子:
import re
pattern1 = re.escape("a*b") # 结果是pattern1 = a\*b
pattern2 = "a\*b" # pattern2和pattern1一样
str = "a*b"
if re.match(pattern1, str) and re.match(pattern2, str):
print("两种方式都可以匹配!")
else:
print("匹配失败!")
用正则表达式切分字符串比用固定的字符更灵活,请看正常的切分代码:
>>> 'a b c'.split(' ')
['a', 'b', '', '', 'c']
嗯,无法识别连续的空格,用正则表达式试试:
>>> re.split(r'\s+', 'a b c')
['a', 'b', 'c']
无论多少个空格都可以正常分割。加入,
试试:
>>> re.split(r'[\s\,]+', 'a,b, c d')
['a', 'b', 'c', 'd']
再加入;
试试:
>>> re.split(r'[\s\,\;]+', 'a,b;; c d')
['a', 'b', 'c', 'd']
除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()
表示的就是要提取的分组(Group)。比如:
^(\d{3})-(\d{3,8})$
分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
如果正则表达式中定义了组,就可以在Match
对象上用group()
方法提取出子串来。
注意到group(0)
永远是与整个正则表达式相匹配的字符串,group(1)
、group(2)
……表示第1、2、……个子串。
最后需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后面的0
:
>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')
由于\d+
采用贪婪匹配,直接把后面的0
全部匹配了,结果0*
只能匹配空字符串了。
必须让\d+
采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0
匹配出来,加个?
就可以让\d+
采用非贪婪匹配:
>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')
当我们在Python中使用正则表达式时,re模块内部会干两件事情:
如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:
>>> import re
# 编译:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')
编译后生成Regular Expression对象,由于该对象自己包含了正则表达式,所以调用对应的方法时不用给出正则字符串。