Python学习7(正则表达式)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习7(正则表达式)相关的知识,希望对你有一定的参考价值。

正则表达式

这里老师讲的有点乱,感觉先看文档再看视频比较好

正则表达式是一个特殊的字符序列,计算机科学的一个概念。通常被用来检索、替换那些符合某个模式(规则)的文本。

许多程序设计语言都支持利用正则表达式进行字符串操作。在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用re模块。re 模块使 Python 语言拥有全部的正则表达式功能。

特点:

  1. 灵活性、逻辑性和功能性非常强;
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。
  3. 对于刚接触的人来说,比较晦涩难懂。

与大多数编程语言相同,正则表达式里也使用\\作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符\\,那么使用编程语言表示的正则表达式里将需要4个反斜杠\\:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。

print(re.match('\\\\\\\\', '\\hello'))  # 需要使用四个反斜杠来匹配一个 \\

Python里的原生字符串很好地解决了这个问题,有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。在Python 字符串前面添加r即可将字符串转换成为原生字符串。

print(re.match(r'\\\\', '\\hello')) # 使用两个反斜杠即可匹配一个 \\

在Python中的查找匹配方法,常见的有下面四种,他们的用法大致相同,但是匹配出的结果却不同。

match方法(只匹配字符串开头)
search方法(扫描整个字符串,找到第一个匹配)
findall方法(扫描整个字符串,找到所有的匹配)
finditer方法(扫描整个字符串,找到所有的匹配,并返回一个可迭代对象)

match

re.match尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

函数语法:

re.match(pattern,string,flags=0)


我们可以使用group(num)函数来获取匹配表达式。

import re
result1 = re.match(r'He','Hello')
result2 = re.match(r'e','Hello')
print(result1.group(0)) # 'He' 匹配到的元素
print(result1.span()) # (0,2) 匹配到的元素所在位置
print(result2)  # None

search

re.search 扫描整个字符串并返回第一个成功的匹配。

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

函数语法:

re.search(pattern, string, flags=0)

示例:

import re
result1 = re.search(r'He','Hello')
result2 = re.search(r'lo','Hello')

print(result1.group(0))  # He
print(result1.span()) # (0,2)
print(result2.group(0)) # lo
print(result2.span()) # (3,5)

s = 'dfa5fdsa'
res = re.search('[0-9]', s) 
print(res) # <re.Match object; span=(3, 4), match='5'>

findall

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

注意: match 和 search 是匹配一次,findall 匹配所有。

语法格式:

re.findall(pattern,string,flags=0)

示例代码:

ret = re.findall(r'\\d+','he23ll34')
print(ret)  # ['23', '34']
ret = re.match(r'\\d+','he23ll34') 
print(ret) # None match只匹配开头,所以匹配到
ret = re.search(r'\\d+','he23ll34')
print(ret) # <re.Match object; span=(2, 4), match='23'> search 只能匹配到一个数字

注意事项:

findall方法匹配时,如果匹配规则里有分组,则只匹配分组数据。

ret = re.findall(r'\\w+@(qq|126|163)\\.com','123@qq.com;aa@163.com;bb@126.com')
print(ret)  # ['qq', '163', '126']  只匹配到了分组里的内容

如果正则表达式里存在多个分组,则会把多个分组匹配成元组。

ret = re.findall(r'\\w+@(qq|126|163)(\\.com)','123@qq.com;aa@163.com;bb@126.com')
print(ret) #[('qq', '.com'), ('163', '.com'), ('126', '.com')]

如果想要让findall匹配所有的内容,而不仅仅只是匹配正则表达式里的分组,可以使用 ?:来将分组标记为非捕获分组。

ret = re.findall(r'\\w+@(?:qq|126|163)\\.com','123@qq.com;aa@163.com;bb@126.com')
print(ret) # ['123@qq.com', 'aa@163.com', 'bb@126.com']

finditer

和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

ret = re.finditer(r'\\d+','he23ll34')  # 得到的结果是一个可迭代对象
for x in ret: # 遍历 ret 取出里面的每一项匹配
    print(x.group(), x.span()) # 匹配对象里的group保存了匹配的结果

re.Match类

当我们调用re.match方法、re.search方法,或者对re.finditer方法的结果进行迭代时,拿到的数据类型都是re.Match对象。

x = re.match(r'h','hello')
y = re.search(r'e','hello')
z = re.finditer(r'l','hello')
print(type(x))  # <class 're.Match'>
print(type(y)) # <class 're.Match'>
for a in z:
    print(type(a)) # <class 're.Match'>

这个类里定义了相关的属性,可以直接让我们来使用。

ret = re.search(r'(abc)+', 'xxxabcabcabcdef')
print(ret.pos)  # 搜索开始的位置,默认是0
print(ret.endpos)  # 搜索结束的位置,默认是字符串的长度 15
print(ret.group(0)) # abcabcabc 匹配整个表达式
print(ret.group(1))  # abc 第一次匹配到的结果
print(ret.span()) # (3, 12) 开始和结束位置
print(ret.groups())  # ('abc',) 表示当正则表达式里有多个分组时,多个分组的匹配结果

re.compile方法

我们在使用正则表达式时,可以直接调用re 模块的 match,search,findall等方法,传入指定的正则表达式。同时,也可以调用re.compile方法,生成一个正则表达式对象,再调用这个正则表达式对象的相关方法实现匹配。

re.match(r'h','hello')  # 可以使用re.match方法直接匹配
# 也可以调用re模块的compile方法,生成一个 Pattern 对象,再调用 Pattern 对象的 match方法
regex = re.compile(r'h')
print(regex.match('hello'))  # <re.Match object; span=(0, 1), match='h'>

re.search(r'l','hello')
regex = re.compile(r'l')
print(regex.match('hello'))  # None

regex = re.compile(r'l')
print(regex.findall('hello')) # ['l', 'l']

regex = re.compile(r'l') 
print(regex.finditer('hello')) # <callable_iterator object at 0x00000232F23D8CC0>

正则表达式修饰符

print(re.search(r'L','hello'))  # None
print(re.search(r'L', 'hello', re.I))  # 不区分大小写<re.Match object; span=(2, 3), match='l'>

# \\w+$ 表示匹配以一个或者多个字母结尾
# re.M 可以进行多行匹配,每个换行都认为是一个结尾
print(re.findall(r'\\w+$','i am boy\\n you are girl\\n he is man',re.M)) # ['boy', 'girl', 'man']
# 不实用re.M修饰符,只会匹配到最后的 man
print(re.findall(r'\\w+$','i am boy\\n you are girl\\n he is man')) # ['man']

print(re.search(r'.','\\n')) # None . 匹配除了 \\n 以外的所有字符
print(re.search(r'.','\\n',re.S)) # '\\n'  匹配到了 \\n

正则表达式模式

模式字符串使用特殊的语法来表示一个正则表达式:

  1. 字母和数字表示他们自身,一个正则表达式模式中的字母和数字匹配同样的字符串。

    re.search(r'H','Hello')  # 这里的 H 表示的就是字母 H 自身,代表有特殊含义
    
  2. 多数字母和数字前加一个反斜杠时会拥有不同的含义。

    ret = re.search(r'\\d','he12ms90') # 这里的 \\d 表示的是匹配数字
    
  3. 标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。

    ret = re.search(r'.','hello') # 这里的 . 表示的是匹配任意字符
    ret = re.search(r'\\.','he.llo')  # 这里的 \\. 进行了转义,才表示标点符号自身。
    
  4. 反斜杠本身需要使用反斜杠转义。由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r’\\t’,等价于\\t )匹配相应的特殊字符。

下表列出了正则表达式模式语法中的特殊元素,如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。

非打印字符

非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:

特殊字符

所谓特殊字符,就是一些有特殊含义的字符。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符\\ 放在它们前面。下表列出了正则表达式中的特殊字符:

定位符

定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。

定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\\b 描述单词的前或后边界,\\B 表示非单词边界。

正则表达式的定位符有:

限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 n 或 n, 或 n,m 共6种。

正则表达式的限定符有:

re.search(r'\\s','大家好 我是 代码')  # 匹配所有的空字符
re.search(r'\\S','大家') # 匹配所有的非空字符
re.search(r'\\n','大家好\\n我是代码') # 匹配换行
re.search(r'n$','hello python') # 匹配以 n 结尾
re.search(r'^h.+n$','hello python') # 匹配以 h 开头,中间出现一次或多次任意字符,并且以n结尾
re.search(r'^ha*','h')  # 匹配以 h 开头,a出现0次或者一次
  1. 用户名匹配:由数字、大小写字母、下划线_和中横线-组成,长度为4到14位,并且不能以数字开头。

    r'^\\D[a-z0-9A-Z_\\-]3,13', 'sH_8'
    print(re.search(r'^\\D(\\w|\\-)3,13$', 'sH_-844646548'))
    
  2. 匹配邮箱

    r'^([A-Za-z0-9_\\-\\.])+@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]2,4)$
    
  3. 匹配手机号

    r'^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d8$'
    
  4. 匹配身份证号。

    r'^[1-9]\\d5(18|19|20|)\\d2((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d3[0-9Xx]$'
    
  5. 匹配URL地址

    r'((ht|f)tps?):\\/\\/([\\w\\-]+(\\.[\\w\\-]+)*\\/)*[\\w\\-]+(\\.[\\w\\-]+)*\\/?(\\?([\\w\\-\\.,@?^=%&:\\/~\\+#]*)+)?'
    
  6. 匹配QQ号

    r'^[1-9][0-9]4,10$'
    
  7. 匹配微信号

    r'^[a-zA-Z]([-_a-zA-Z0-9]5,19)+$'
    
  8. 匹配车牌号

    r'^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]1[A-Z]1[A-Z0-9]4[A-Z0-9挂学警港澳]1$'
    

分组

用()进行分组,1表示第一组,0表示匹配的整体

# 分组提取
phone = '010-12345678'
res = re.match('(\\d3,4)-(\\d8)', phone)
print(res)
print(res.group())  # 010-12345678
print(res.group(0)) # 010-12345678
print(res.group(1)) # 010   1表示第一组
print(res.group(2)) # 12345678  2表示第二组

引用分组的内容,使用数字\\number 或者给分组起名 (?P<name>)

msg = '<html>abc</html>'
msg1 = '<html>abc</h1>'
res = re.match(r'<\\w+>(.+)</\\w+>', msg)
print(res) # <re.Match object; span=(0, 15), match='<html>abc<html>'>
print(res.group(1)) # abc

res = re.match(r'<\\w+>(.+)</\\w+>', msg1)
print(res) # <re.Match object; span=(0, 13), match='<html>abc<h1>'>
print(res.group(1)) # abc

# 怎么样才能使得前后标签一致呢
res = re.match(r'<(\\w+)>(.+)</\\1>', msg1)
print(res) # None
res = re.match(r'<(\\w+)>(.+)</\\1>', msg)
print(res) # None
print(res.group(1))  # html
print(res.group(2))  # abc

# 给分组起名
msg2 = '<html><h1>abc</h1></html>'
res = re.match(r'<(\\w+)><(\\w+)>(.+)</\\2></\\1>', msg2)
print(res) # <re.Match object; span=(0, 25), match='<html><h1>abc</h1></html>'>

# # 用(?P<名字>正则表达式)给分组起名, 使用(?P=名字)使用这个分组
res = re.match(r'<(?P<name1>\\w+)><(?P<name2>\\w+)>(.+)</(?P=name2)></(?P=name1)>', msg2)
print(res)  # <re.Match object; span=(0, 25), match='<html><h1>abc</h1></html>'>

替换sub

Python中的re模块提供了re.sub用户替换字符串中的匹配项。

语法:

re.sub(pattern,repl,string,count=0)

参数:

  • pattern : 正则中的模式字符串。
  • repl : 替换的字符串,也可为一个函数。
  • string : 要被查找替换的原始字符串。
  • count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
phone = "2004-959-559 # 这是一个电话号码"
# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)	# # 电话号码 :  2004-959-559

# 移除非数字的内容
num = re.sub(r'\\D', "", phone)
print ("电话号码 : ", num) # 电话号码 :  2004959559

repl可以使用一个字符串用来表示替换后的结果以外,还可以传入一个函数。

# 将数字乘2
def double(matched):
    test = int(matched.group('test'))
    return str(test * 2)

# 用起名的方式取出分组,然后将分组作为参数传入函数,调用函数将数字乘以2
# 注意参数为函数的时候不写括号
print(re.sub(r'(?P<test>\\d+)', double, 'hello23hi34'))  # hello46hi68

切割split

按照正则表达式分割,将结果放在一个列表中

msg = 'java:99,python:100'
print(re.split('[:,]', msg)) # ['java', '99', 'python', '100']

贪婪匹配和非贪婪匹配

Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;

非贪婪则相反,总是尝试匹配尽可能少的字符。

在*,?,+,m,n后面加上 ?使贪婪变成非贪婪。

可以看到,加上?以后,只匹配一个数字就不往后匹配了

s = 'abc1234'
r = re.match(r'abc(\\d+)', s)
print(r)  # <re.Match object; span=(0, 7), match='abc1234'>
print(r.group(1))  # 1234
r = re.match(r'abc(\\d+?)', s)  
print(r) # <re.Match object; span=(0, 4), match='abc1'>
print(r.group(1)) # 1

下面这个例子中:
正则表达式模式中使用到通配字,那它在从左到右的顺序求值时,会尽量“抓取”满足匹配最长字符串,在例子里面,“.+”会从字符串的启始处抓取满足模式的最长字符,其中包括我们想得到的第一个整型字段的中的大部分,“\\d+”只需一位字符就可以匹配,所以它匹配了数字“4”,而“.+”则匹配了从字符串起始到这个第一位数字4之前的所有字符。
而当加上?以后,匹配到可以满足条件就结束了,所以.+匹配到了数字前面就停止了

s = "This is a number 234-235-22-423"
r = re.match(".+(\\d+-\\d+-\\d+-\\d+)", s)
print(r)  # <re.Match object; span=(0, 31), match='This is a number 234-235-22-423'>
print(r.group(1))
# '4-235-22-423'
r = re.match(".+?(\\d+-\\d+-\\d+-\\d+)", s)
print(r)  # <re.Match object; span=(0, 31), match='This is a number 234-235-22-423'>
print(r.group(1))
# '234-235-22-423'
>>> re.match(r"aa(\\d+)","aa2343ddd").group(1)
'2343'
>>> re.match(r"aa(\\d+?)","aa2343ddd").group(1)
'2'
>>> re.match(r"aa(\\d+)ddd","aa2343ddd").group(1) 
'2343'
>>> re.match(r"aa(\\d+?)ddd","aa2343ddd").group(1)
'2343'

以上是关于Python学习7(正则表达式)的主要内容,如果未能解决你的问题,请参考以下文章

python与正则表达式

Python正则表达式--字符串匹配开头结尾

python学习day19正则模块

以 7 位数字结尾的字段的 Big Query SQL 正则表达式

Day 26 python 正则表达式

Python学习7(正则表达式)