正则表达式
Table of Contents
正则表达式是一个独立的命题,而非python独有。在python中可以使用内建的re模块来使用相关的功能实践。
- 正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
我们常用的有:
- match 一个文本是否满足给定的正则表达
- search 得到文本中的第一个满足正则的结果
- findall 获取文本中所有满足正则表达的结果
使用之前我们需要先导入re模块:
import re
match
match()
函数总数从文本的*开头开始*,返回满足的结果或者None
注意我们需要实现精准匹配时必须使用$,作为末尾标记。这一函数最经典的应用莫过于筛选满足格式的文件!
从头开始
如果我们的匹配正则表达不满足“从头开始”,是一定无法匹配成功的:
sentence = 'x123-346-9765' # 开头非字符
number_regex = re.compile(r'\d{3}-\d{3}-\d{4}')
mo = number_regex.match(sentence)
if mo: print(mo.group())
else: print('Not Matched!')
Not Matched!
不使用$带来的问题:
sentence = '536-836-7435'
# \d表示数字 {3}表示前面的表达出现3次
number_regex = re.compile(r'\d{3}-\d{3}-\d{4}') # compile将字符串正则化,代替方案可以直接用re.match
mo = number_regex.match(sentence)
if mo: print(mo.group())
else: print('Not Matched!')
536-836-7435
乍一看没什么问题, 但是不要忘记没有标记$,意味着重头开始匹配,那么如果满足结果的后面有更多的文本那么就意味着失败!
# .表示任意字符 + 表示前面的正则表达重复1次及1次以上
csv_regex = re.compile(r'.+csv')
mo = csv_regex.match('test.csv')
if mo: print(mo.group())
else: print('Not Matched!')
mo = csv_regex.match('test.csv.xls') # 由于没有文本结尾标记我们发现不是我们要的结果
if mo: print(mo.group())
else: print('Not Matched!')
test.csv
test.csv
# 将.转义,说明以.csv结尾的文本
csv_regex = re.compile(r'.+\.csv$') # 使用文本结尾标记
mo = csv_regex.match('test.csv')
if mo: print(mo.group())
else: print('Not Matched!')
mo = csv_regex.match('test.csv.xls')
if mo: print(mo.group())
else: print('Not Matched!')
test.csv
Not Matched!
search
返回第一个满足匹配的结果,但是区别于match,并不严格要求从文本开头时就开始匹配
sentence = 'x123-346-9765' # 开头非字符
number_regex = re.compile(r'\d{3}-\d{3}-\d{4}')
mo = number_regex.search(sentence) # 搜索可以完成
if mo: print(mo.group())
else: print('Not Matched!')
123-346-9765
返回第一个
sentence = 'x123-346-9765,357-768-3467'
number_regex = re.compile(r'\d{3}-\d{3}-\d{4}')
mo = number_regex.search(sentence)
if mo: print(mo.group())
else: print('Not Matched!')
123-346-9765
group()??
在我学习之初我也不理解这个有什么用,现在开始理解将正则表达自己想提取的部分用()标记,可以实现分组:
sentence = 'x123-346-9765,357-768-3467'
number_regex = re.compile(r'(\d{3})-(\d{3})-(\d{4})')
mo = number_regex.search(sentence)
if mo: print(mo.groups())
else: print('Not Matched!')
# first, mid, last = mo.groups()
('123', '346', '9765')
findall
对于上面的例子如果我们要找出所有的匹配结果那么,我们可以使用findall来进行:
sentence = 'x123-346-9765,357-768-3467'
number_regex = re.compile(r'\d{3}-\d{3}-\d{4}')
mo = number_regex.findall(sentence)
for item in mo:
print(item)
123-346-9765
357-768-3467
是否可以实现()提取效果?
sentence = 'x123-346-9765,357-768-3467'
number_regex = re.compile(r'(\d{3})-(\d{3})-(\d{4})')
mo = number_regex.findall(sentence)
for first, mid, last in mo:
print(f'first:{first}, mid:{mid}, last:{last}')
first:123, mid:346, last:9765
first:357, mid:768, last:3467
正则中的特殊字符
字符 | 描述 |
---|---|
. | 在默认模式下,它匹配除换行以外的任何字符。如果指定了DOTALL标志,它将匹配包括换行符在内的任何字符。 |
^ | 匹配字符串的开头,并且在多行模式下也在每个换行之后立即匹配。 |
$ | 匹配字符串的末尾,或者刚好在字符串末尾的换行之前,在多行模式下也匹配换行之前。 |
* | 匹配前一个RE的0次或多次重复,重复次数越多越好。 |
+ | 前面的RE匹配1次或更多次。 |
? | 前面的RE匹配0次或1次。 |
{m} | 指定要匹配前一个RE的重复m次。 |
{m,n} | 匹配前一个RE的m到n次重复,尝试匹配尽可能多的重复。 |
+?, *?, ?? | +, *, ?都是贪婪的(尽可能匹配多的字符),假设?后可以限定成为非贪婪模式。 |
{m,n}? | 前面的RE匹配m到n次,尝试匹配尽可能少的重复。 |
\ | 转义特殊字符(允许您匹配字符,如“*”、“?”等)或发出特殊序列的信号。 |
[] | 指示一组字符,注意在里面使用^表示非逻辑,特殊字符’]'需要转义或放在开始位置。 |
由于转义和特殊字符强烈建议正则表达使用原生字符串!
'\'的特殊序列
表达式 | 描述 |
---|---|
\d | 匹配任何Unicode十进制数字。这包括[0-9],以及许多其他数字字符。如果只使用ASCII标志,则匹配[0-9]。 |
\D | \d相反, 匹配任何非十进制数字。 |
\s | 匹配Unicode下的空白字符包括空格,\t、\n、\r、\f、\v。 |
\S | \s相反,匹配除空格,\t、\n、\r、\f、\v之外的任意字符。 |
\w | Unicode字符下,这包括大多数可以作为任何语言中单词一部分的字符,以及数字和下划线。如果使用ASCII标志,则仅匹配[a-zA-Z0-9]。 |
\W | \w相反。 |
能够感觉到ASCII或者说编码带来的不便,具体程度还没有体会到!
正则标志
标志 | 说明 |
---|---|
re.I, re.IGNORECASE | 执行不区分大小写的匹配。 |
re.A, re.ASCII | Unicode编码下\w,\w,\b,\B,\d,\D,\s和\S只执行ASCII匹配。 |
re.S, re.DOTALL | “.”特殊字符匹配任何字符,包括换行符。 |
好用的正则函数函数
re.split()
使用正则切割字符串:
DT = '2019-12-13 12:34:45'
re.split('[- :]', DT) # 提取年月日等信息
['2019', '12', '13', '12', '34', '45']
re.sub()
使用正则批量替换:
s = 'py th on'
re.sub(r'\s+', r'', s) # 去除所有的空白字符
'python'
s = "价格是:23.45元, 重量:33.34KG"
re.sub(r'([\d\.]+)', r'(\1)', s) # 将匹配到的结果添加在()内,然后替换
'价格是:(23.45)元, 重量:(33.34)KG'