【转】Python 正则表达式模块 (re) 简介

Python 的 re 模块(Regular Expression 正则表达式)提供各种正则表达式的匹配操作,和 Perl 脚本的正则表达式功能类似,使用这一内嵌于 Python 的语言工具,尽管不能满足所有复杂的匹配情况,但足够在绝大多数情况下能够有效地实现对复杂字符串的分析并提取出相关信息。Python 会将正则表达式转化为字节码,利用 C 语言的匹配引擎进行深度优先的匹配。

表 1. 正则表达式元字符和语法
在这里插入图片描述
在这里插入图片描述
表 2. 正则表达式特殊序列
在这里插入图片描述

import re
#贪婪
ret_greed= re.findall(r'a(\d+)','a23b')
print(ret_greed)
#非贪婪
ret_no_greed= re.findall(r'a(\d+?)','a23b')
print(ret_no_greed)
 
['23']
['2']

由于贪婪匹配为尽可能的多匹配所以结果为23 ,有人好奇了,findall是什么鬼 ,请耐心往下看:

re模块

正则表达式使用反斜杠” \ “来代表特殊形式或用作转义字符,这里跟Python的语法冲突,因此,Python用” \ “表示正则表达式中的” \ “,因为正则表达式中如果要匹配” \ “,需要用\来转义,变成” \ “,而Python语法中又需要对字符串中每一个\进行转义,所以就变成了” \ “。

上面的写法是不是觉得很麻烦,为了使正则表达式具有更好的可读性,Python特别设计了原始字符串(raw string),需要提醒你的是,在写文件路径的时候就不要使用raw string了,这里存在陷阱。raw string就是用’r’作为字符串的前缀,如 r”\n”:表示两个字符”\”和”n”,而不是换行符了。Python中写正则表达式时推荐使用这种形式。

1、 re.findall(pattern, string[, flags]):

方法能够以列表的形式返回能匹配的子串。先看简单的例子:

import re
a = 'one1two2three3four4'
ret = re.findall(r'(\d+)',a)
print(ret)
['1', '2', '3', '4']

2、re.finditer(pattern, string[, flags])

搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。 请看例子:

import re
  
p = re.compile(r'\d+')
for m in p.finditer('one1two2three3four4'):
    print m.group(),
 
### output ###
# 1 2 3 4 

3、re.match和re.search

Python提供了两种不同的原始操作:match和search。match是从字符串的起点开始做匹配,而search(perl默认)是从字符串做任意匹配。看个例子:

import re
 
ret_match= re.match("c","abcde");     #从字符串开头匹配,匹配到返回match的对象,匹配不到返回None
if(ret_match):
    print("ret_match:"+ret_match.group());
else:
    print("ret_match:None");
 
ret_search = re.search("c","abcde"); #扫描整个字符串返回第一个匹配到的元素并结束,匹配不到返回None
if(ret_search):
    print("ret_search:"+ret_search.group());
 
ret_match:None
ret_search:c

re.match对象拥有以下方法:

import re
a = "123abc456"
ret_match= re.match("a","abcde");
print(ret_match.group())  #返回返回被 RE 匹配的字符串
print(ret_match.start())  #返回匹配开始的位置
print(ret_match.end())    #返回匹配结束的位置
print(ret_match.span())   #返回一个元组包含匹配 (开始,结束) 的位置

其中group()方法可以指定组号,如果组号不存在则返回indexError异常看如下例子:

import re
a = "123abc456"
re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)   #123abc456,返回整体默认返回group(0)
re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)   #123
re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)   #abc
re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)   #456

4、re.sub和re.subn

两种方法都是用来替换匹配成功的字串,值得一提的时,sub不仅仅可以是字符串,也可以是函数。subn函数返回元组,看下面例子:

import  re
#sub
ret_sub = re.sub(r'(one|two|three)','ok','one word two words three words') 
#ok word ok words ok words
#subn
import  re
ret_subn = re.subn(r'(one|two|three)','ok','one word two words three words') 
#('ok word ok words ok words', 3) 3,表示替换的次数

5、re.split(pattern, string, maxsplit=0)

通过正则表达式将字符串分离。如果用括号将正则表达式括起来,那么匹配的字符串也会被列入到list中返回。maxsplit是分离的次数,maxsplit=1分离一次,默认为0,不限制次数。看一下例子:

import re
ret = re.split('\d+','one1two2three3four4') 
#匹配到1的时候结果为'one'和'two2three3four4',匹配到2的时候结果为'one', 'two'和'three3four4', 所以结果为:
####output####
['one', 'two', 'three', 'four', '']

6、re.compile(strPattern[, flag])

这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。第二个参数flag是匹配模式,取值可以使用按位或运算符’|’表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile(‘pattern’, re.I | re.M)与re.compile(‘(?im)pattern’)是等价的。可选值有:

re.I(IGNORECASE): 忽略大小写(括号内是完整写法,下同)
re.M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图)
re.S(DOTALL): 点任意匹配模式,改变'.'的行为
re.L(LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
re.U(UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
re.X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。

请看例子:

import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
regex = re.compile(r'\w*oo\w*')
print(regex.findall(text))
 
['JGood', 'cool']

re 小结

#re.match()
#re.search()
#re.findall()
#re.split()
#re.sub()
a = 'hello word diao zha tian'
ret = re.match('h\w+',a)
print(ret.group()) #获取匹配到的所有结果
print(ret.groups()) # 获取模型中匹配到的分组情况
print(ret.groupdict())# 获取模型中匹配到的分组结果
############output##############
hello
()
{}

ret = re.match('(?P<n>h)(?P<n1>\w+)',a)
print(ret)
print(ret.group()) #获取匹配到的所有结果
print(ret.groups()) # 获取模型中匹配到的分组情况
print(ret.groupdict())# 获取模型中匹配到的分组结果
#?P<n1> = Key
############output##############

<_sre.SRE_Match object; span=(0, 5), match='hello'>
hello
('h', 'ello')
{'n': 'h', 'n1': 'ello'}
#re.findall()

a = 'hello alex alex adn acd'
n = re.findall('(a)(\w+)',a)  
#[('a', 'lex'), ('a', 'lex'), ('a', 'dn'), ('a', 'cd')]
print(n)                            
#从左到右,从外到内 ,?P<> 无效
#re.split()

#在编写计算器的时候可以用re.split() 比如:

def f1(ex):
    return eval(ex)  #测试用 真实中要自己编写四则运算
 
a = '1*2+(5/6)+(12*23)/15'
while True:
    ret = re.split('\(([^()]+)\)', a, 1)
    if len(ret) == 3:
        a,b,c = re.split('\(([^()]+)\)', a, 1)
        rec = f1(b)
        a = a + str(rec) + c
    else:
        red = f1(a)
        print(red)
        break
re.sub()  用于替换匹配的字符串


content = "123abc456"
new_content = re.sub('\d+', 'sb', content)
# new_content = re.sub('\d+', 'sb', content, 1)
print new_content 
 常用的验证规则:


验证手机号:
(^(13\d|14[57]|15[^4\D]|17[13678]|18\d)\d{8}|170[^346\D]\d{7})

验证邮箱:
^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$


IP:
^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$

计算器源码:

import re
def md(date_list,symbol):
    '''

    :param date_list: 匹配到的表达式
    :param symbol: 符号
    :return: 乘数计算得到的值
    '''
    a = date_list.index(symbol)  #取到符号
    if symbol == '*' and date_list[a + 1] != '-': #如果是乘号并且索引的下一个位置不是负号计算
        k = float(date_list[a - 1]) * float(date_list[a + 1])
    elif symbol == '/' and date_list[a + 1] != '-':  #如果是除号并且索引的下一个位置不是负号计算
        k = float(date_list[a - 1]) / float(date_list[a + 1])
    elif symbol == '*' and date_list[a + 1] == '-': #如果是乘号并且索引的下一个位置是负号计算
        k = -(float(date_list[a - 1]) * float(date_list[a + 2]))
    elif symbol == '/' and date_list[a + 1] == '-': #如果是除号并且索引的下一个位置是负号计算
        k = -(float(date_list[a - 1]) / float(date_list[a + 2]))
    del date_list[a - 1], date_list[a - 1], date_list[a - 1] #删除列表里参与计算的索引位置
    date_list.insert(a - 1, str(k))  #把新的值插入到列表中
    return date_list
#处理混乱的四则,按照先算加减后乘除的原则
def fun(s):
    '''

    :param s: 去除括号后的表达式
    :return: 表达式的返回值
    '''
    list_str = re.findall('([\d\.]+|/|-|\+|\*)',s) #匹配表达式
    sum=0
    while 1:
        if '*' in list_str and '/' not in list_str:  #判断乘是否在表达式内
            md(list_str, '*')
        elif '*' not in list_str and '/' in list_str: #判断乘是否在表达式内
            md(list_str, '/')                   #调用md函数处理除号
        elif '*' in list_str and '/' in list_str:
            a = list_str.index('*')
            b = list_str.index('/')
            if a < b:
                md(list_str, '*')
            else:
                md(list_str, '/')
        else:
            if list_str[0]=='-':   #判断是否是负号
                list_str[0]=list_str[0]+list_str[1]
                del list_str[1]
            sum += float(list_str[0])
            for i in range(1, len(list_str), 2):
                if list_str[i] == '+' and list_str[i + 1] != '-':
                    sum += float(list_str[i + 1])
                elif list_str[i] == '+' and list_str[i + 1] == '-':
                    sum -= float(list_str[i + 2])
                elif list_str[i] == '-' and list_str[i + 1] == '-':
                    sum += float(list_str[i + 2])
                elif list_str[i] == '-' and list_str[i + 1] != '-':
                    sum -= float(list_str[i + 1])
            break
    return sum



a='1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
#循环去除括号
while True:
    ret = re.split('\(([^()]+)\)', a, 1)
    if len(ret) == 3:
        a,b,c = re.split('\(([^()]+)\)', a, 1)
        rec = fun(b)
        a = a + str(rec) + c

    else:
        red = fun(a)
        print(red)
        break

猜你喜欢

转载自blog.csdn.net/qq_38970783/article/details/84976646