Python——re模块

re模块

正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re。

元字符

元字符
 
匹配内容
匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线
\s 匹配任意的空白符
\d 匹配数字
\n 匹配一个换行符
\t 匹配一个制表符
\b 匹配一个单词的结尾
^ 匹配字符串的开始
$ 匹配字符串的结尾
\W
匹配非字母或数字或下划线
\D
匹配非数字
\S
匹配非空白符
a|b
匹配字符a或字符b
()
匹配括号内的表达式,也表示一个组
[...]
匹配字符组中的字符
[^...]
匹配除了字符组中字符的所有字符

量词:

量词
用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

字符组 : [字符组]

在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。

正则
待匹配字符
匹配
结果
说明
[0123456789]
8
True
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
和"待匹配字符"相同都视为可以匹配
[0123456789]
a
False
由于字符组中没有"a"字符,所以不能匹配
 
[0-9]
 
7
True
也可以用-表示范围,[0-9]就和[0123456789]是一个意思
 
[a-z]
 
s
 
True
 
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示
 
[A-Z]
 
B
 
True
 
[A-Z]就表示所有的大写字母
 
[0-9a-fA-F]
 
e
 
True
 
可以匹配数字,大小写形式的a~f,用来验证十六进制字符

 匹配开头和结尾

>>> s = 'xyzd2asd'
>>> re.search('^xy',s)
<_sre.SRE_Match object; span=(0, 2), match='xy'>
>>> re.search('sd$',s)
<_sre.SRE_Match object; span=(6, 8), match='sd'>
>>> re.search('y$',s)
>>> 

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\\'。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。

所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了

正则 待匹配字符 匹配
结果
说明
\d \d  False
因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配
\\d \d  True
转义\之后变成\\,即可匹配
"\\\\d" '\\d'  True
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次
r'\\d' r'\d'  True
在字符串之前加r,让整个字符串不转义

 贪婪与非贪婪模式

正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的,总是尝试匹配尽可能多的字符;

非贪婪则相反,总是尝试匹配尽可能少的字符。在"*","?","+","{m,n}"后面加上?,使贪婪变成非贪婪。

>>> s = '<html>i love python</html>'
>>> re.findall('<html>.*?',s)
['<html>']
>>> re.findall('<html>.*',s)
['<html>i love python</html>']
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现

Flags标志符

flags有很多可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释
>>> s1 =  '<html>i love python</HTML>'
>>> re.findall('ht',s1,re.I)
['ht', 'HT']
>>> re.findall('ht',s1)
['ht']

re模块中常用功能函数

 1、compile()

编译正则表达式模式,返回一个对象的模式。(可以把那些常用的正则表达式编译成正则表达式对象,这样可以提高一点效率。)

格式:

re.compile(pattern,flags=0)

pattern: 编译时用的表达式字符串。

>>> import re
>>> s = 'woxi is a good girl,she is cool,clever,and so on'
>>> com_str = re.compile(r'g[a-z]{0,3}')
>>> com_str.findall(s) 
[
'good', 'girl']

2、match()

决定RE是否在字符串刚开始的位置匹配。//注:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符'$'

格式:

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

>>> print(re.match('com','comwww').group())
com
>>> print(re.match('com','Comwww',re.I).group())
Com

3、search()

 格式:

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

re.search函数会在字符串内查找模式匹配,只要找到第一个匹配然后返回,如果字符串没有匹配,则返回None。

>>> print(re.match('com','comwww').group())
com

*注:match和search一旦匹配成功,就是一个match object对象,而match object对象有以下方法:

  • group() 返回被 RE 匹配的字符串
  • start() 返回匹配开始的位置
  • end() 返回匹配结束的位置
  • span() 返回一个元组包含匹配 (开始,结束) 的位置
  • group() 返回re整体匹配的字符串,可以一次输入多个组号,对应组号匹配的字符串。

a. group()返回re整体匹配的字符串,
b. group (n,m) 返回组号为n,m所匹配的字符串,如果组号不存在,则返回indexError异常
c.groups()groups() 方法返回一个包含正则表达式中所有小组字符串的元组,从 1 到所含的小组号,通常groups()不需要参数,返回一个元组,元组中的元就是正则表达式中定义的组。 

import re
a = "123abc456"
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).groups())   #('123', 'abc', '456'),返回匹配的所有分组
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0))   #123abc456,返回整体
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1))   #123
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2))   #abc
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3))   #456
###group(1) 列出第一个括号匹配部分,group(2) 列出第二个括号匹配部分,group(3) 列出第三个括号匹配部分。

4、findall()

re.findall遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表。

 格式:

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

s= re.compile(r'\d+')
print(s.findall('o1w2a3'))
# 执行结果如下:
['1', '2', '3']
import re
t = "Tina is a good girl, she is cool, clever, and so on..."
r = re.compile(r'\w*oo\w*')
print(r.findall(t))
print(re.findall(r'(\w)*oo(\w)',t))#()表示子表达式 
# 执行结果如下:
['good', 'cool']
[('g', 'd'), ('c', 'l')]

5、finditer()

 搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。

格式:re.finditer(pattern, string, flags=0)

iter = re.finditer(r'\d+','12 aa33bb,11cc10dd')
for i in iter:
    print(i)
    print(i.group())
    print(i.span())
    
# <_sre.SRE_Match object; span=(0, 2), match='12'>
# 12
# (0, 2)
# <_sre.SRE_Match object; span=(5, 7), match='33'>
# 33
# (5, 7)
# <_sre.SRE_Match object; span=(10, 12), match='11'>
# 11
# (10, 12)
# <_sre.SRE_Match object; span=(14, 16), match='10'>
# 10
# (14, 16)

6、split()

按照能够匹配的子串将string分割后返回列表。

可以使用re.split来分割字符串,如:re.split(r'\s+', text);将字符串按空格分割成一个单词列表。

格式:

re.split(pattern, string[, maxsplit])

maxsplit用于指定最大分割次数,不指定将全部分割。

print(re.split('\d+','one1two2three3four4five5'))
执行结果如下:
['one', 'two', 'three', 'four', 'five', '']

7、sub()

使用re替换string中每一个匹配的子串后返回替换后的字符串。

格式:

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

import re
text = "shuke is a handsome boy, he is cool, clever, and so on..."
print(re.sub(r'\s+', ':', text))
# 执行结果如下:
#JGood:is:a:handsome:boy,:he:is:cool,:clever,:and:so:on...
# 其中第二个函数是替换后的字符串;本例中为':'
# 第四个参数指替换个数。默认为0,表示每个匹配项都替换。

re.sub还允许使用函数对匹配项的替换进行复杂的处理。

import re

s = '1267'

def replace_digit(m):
    ss = '零一二三四五六七八九'
    index = int(m.group())
    return ss[index]

result = re.sub(u'\d', replace_digit, s, count=4)

print(result)
# 一二六七

re模块的一些小应用

匹配中文

>>> s1='ss我们必须成为大海aabbb'
>>> re.findall('[\u4e00-\u9fa5]+',s1)
['我们必须成为大海']
>>> 

匹配IP

IP地址分四段,每段数字范围为0-255,段与段之间用英文句点'.'隔开
其中,
250-255: 特点:三位数,百位数是2,十位是5,个位是0-5,用正则表达式可以写成:25[0-5]
200-249: 特点:三位数,百位数是2,十位是0-4,个位是0-9,用正则表达式可以写成:2[0-4]\d
100-199:特点: 三位数,百位数是1,十位,个位是0-9,用正则表达式可以写成:1\d{2}
10-99:特点:二位数,十位是1-9,个位数0-9,用正则表达式可以写成:[1-9]\d
0-9:特点:一位数,用正则表达式可以写成:\d
 
综上:可以将0-99的正则表达式写成:[1-9]?\d (?表示前一个字符0次或1次扩展)

 ((1\d{2}|25[0-5]|2[0-4]\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)

import re
right=r'((1\d{2}|25[0-5]|2[0-4]\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)'
n=re.search(right,'192.168.1.5')
print(n.group(0))
# 192.168.1.5

匹配手机号

r_phone = r'^(13|14|15|18)[0-9]{9}$'
n=re.search(r_phone,'18910171654')
print(n.group(0))
#18910171654

匹配邮箱

r_mail=r'^[a-zA-Z0-9]+@(\w+)\.(\w+)$'
n=re.search(r_mail,'[email protected]')
print(n.group(0))
#[email protected]

案例:实现简单计算器

计算(9-2*5 + 3*99*4*2998)/3+(5/2)

import re
# cal_s = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'

multi_div_re = r'-{0,1}\d+\.?\d*[*/]-?\d+\.?\d*'
# 乘除法正则
add_minus_re = r'-{0,1}\d+\.?\d*[+-]-?\d+\.?\d*'
# 加减法正则
bracket_re = r'\([^()]+\)'
# 括号正则

def remove_brack(express_str):
   express_str = re.sub('\s','',express_str)
   while True:
        s_brack = re.search(bracket_re, express_str.strip())
        if s_brack:
            re_exp = s_brack.group()
            print('re_exp=', re_exp)
            # 得到去除括号的表达式
            cal_res_mid = cal_exp(re_exp.strip('()'))  # 乘除法
            cal_res = cal_exp(cal_res_mid,add_minus_re) # 加减法
            # 计算表达式
            print('str=', cal_res)
            express_str = deal_symbol(express_str.replace(re_exp,str(cal_res)))
            print('express_str=', express_str)
            continue
        break

   res_mid = cal_exp(express_str)  # 乘除法
   res = cal_exp(res_mid, add_minus_re)  # 加减法
   return  res


def cal_exp(express_str,re_str = multi_div_re):
    """
    计算表达式
    :param express_str: 
    :param re_str: 
    :return: 
    """
    while True:
        re_exp = re.search(re_str,express_str)
        if re_exp:
            res_exp = re_exp.group()
            # print(res_exp)
            res = cal_str(res_exp)
            express_str = deal_symbol(express_str.replace(res_exp,str(res)))
            print('cal_exp=',express_str)
            continue
        break
    return express_str

def deal_symbol(express_str):
    """
    处理符号
    :param express_str: 
    :return: 
    """
    if '--' in express_str:
        # print('--')
        express_str = express_str.replace('--','-')
    elif '+-' in express_str:
        # print('+-')
        express_str = express_str.replace('+-','-')
    elif '++' in express_str:
        # print('++')
        express_str = express_str.replace('++','+')
    elif '-+' in express_str:
        # print('-+')
        express_str = express_str.replace('-+','-')
    # print('deal_symbol=',express_str)
    return express_str

def cal_str(express_str):
    """
    计算2个元素的表达式的值
    5+2
    5*2
    -5-2
    5-2
    5/2
    :param express_str: 
    :return: 
    """
    res = 0.0
    # print(express_str)
    if '*' in express_str:
        x, y = express_str.split('*')
        res = round((float(x) * float(y)),2)
    elif '/' in express_str:
        x, y = express_str.split('/')
        res = round((float(x) / float(y)),2)
    elif '+' in express_str:
        x, y = express_str.split('+')
        res = round((float(x) + float(y)),2)
    elif '-' in express_str and express_str[0] == '-':
        # 处理-5-2
        x, y,z = express_str.split('-')
        res = round((-float(y) - float(z)),2)
    elif '-' in express_str and express_str[0] !='-':
        # 处理5-2
        x, y = express_str.split('-')
        res = round((float(x) - float(y)),2)
    return res


if __name__ == '__main__':
    # cal_exp('1386418.32-1.2',add_minus_re)
    print(remove_brack(('(9-2*5 + 3*99*4*2998)/3+(5/2)')))

猜你喜欢

转载自www.cnblogs.com/xiao-apple36/p/8935783.html
今日推荐