文档章节

Python系列之正则表达式详解

Airship
 Airship
发布于 2018/03/28 10:21
字数 2975
阅读 16
收藏 5

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

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

表 1. 正则表达式元字符和语法

符号 说明 实例
. 表示任意字符,如果说指定了 DOTALL 的标识,就表示包括新行在内的所有字符。 'abc'  >>>'a.c'   >>>结果为:'abc' 
^ 表示字符串开头。 'abc'  >>>'^abc'  >>>结果为:'abc'
$ 表示字符串结尾。 'abc'  >>>'abc$'  >>>结果为:'abc'
*, +, ? '*'表示匹配前一个字符重复 0 次到无限次,'+'表示匹配前一个字符重复 1次到无限次,'?'表示匹配前一个字符重复 0 次到1次

'abcccd'  >>>'abc*' >>>结果为:'abccc'

'abcccd' >>>'abc+'  >>>结果为:'abccc'

'abcccd' >>>'abc?'  >>>结果为:'abc'

*?, +?, ??    前面的*,+,?等都是贪婪匹配,也就是尽可能多匹配,后面加?号使其变成惰性匹配即非贪婪匹配

'abc'  >>>'abc*?' >>>结果为:'ab'

'abc'  >>>'abc??' >>>结果为:'ab'

'abc'  >>>'abc+?' >>>结果为:'abc'

{m} 匹配前一个字符 m 次 'abcccd' >>>'abc{3}d'  >>>结果为:'abcccd'
{m,n} 匹配前一个字符 m 到 n 次 'abcccd'  >>> 'abc{2,3}d' >>>结果为:'abcccd'
{m,n}? 匹配前一个字符 m 到 n 次,并且取尽可能少的情况  'abccc'  >>> 'abc{2,3}?' >>>结果为:'abcc'
\ 对特殊字符进行转义,或者是指定特殊序列  'a.c' >>>'a\.c' >>> 结果为: 'a.c'
[]                  表示一个字符集,所有特殊字符在其都失去特殊意义,只有: ^  -  ]  \   含有特殊含义 'abcd' >>>'a[bc]' >>>结果为:'ab'
| 或者,只匹配其中一个表达式 ,如果|没有被包括在()中,则它的范围是整个正则表达式 'abcd' >>>'abc|acd' >>>结果为:'abc'
( … ) 被括起来的表达式作为一个分组. findall 在有组的情况下只显示组的内容  'a123d' >>>'a(123)d' >>>结果为:'123'
(?#...) 注释,忽略括号内的内容  特殊构建不作为分组  'abc123' >>>'abc(?#fasd)123' >>>结果为:'abc123'
(?= … ) 表达式’…’之前的字符串,特殊构建不作为分组 在字符串’ pythonretest ’中 (?=test) 会匹配’ pythonre ’
(?!...) 后面不跟表达式’…’的字符串,特殊构建不作为分组 如果’ pythonre ’后面不是字符串’ test ’,那么 (?!test) 会匹配’ pythonre ’
(?<= … ) 跟在表达式’…’后面的字符串符合括号之后的正则表达式,特殊构建不作为分组 正则表达式’ (?<=abc)def ’会在’ abcdef ’中匹配’ def ’
(?:) 取消优先打印分组的内容 'abc' >>>'(?:a)(b)' >>>结果为'[b]'
?P<> 指定Key 'abc' >>>'(?P<n1>a)>>>结果为:groupdict{n1:a}

表 2. 正则表达式特殊序列

特殊表达式序列 说明
\A 只在字符串开头进行匹配。
\b 匹配位于开头或者结尾的空字符串
\B 匹配不位于开头或者结尾的空字符串
\d 匹配任意十进制数,相当于 [0-9]
\D 匹配任意非数字字符,相当于 [^0-9]
\s 匹配任意空白字符,相当于 [ \t\n\r\f\v]
\S 匹配任意非空白字符,相当于 [^ \t\n\r\f\v]
\w 匹配任意数字和字母,相当于 [a-zA-Z0-9_]
\W 匹配任意非数字和字母的字符,相当于 [^a-zA-Z0-9_]
\Z 只在字符串结尾进行匹配

上面提到贪婪匹配和非贪婪匹配请看例子:

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()

re.match()、re.search()可以分为两种情况,第一种不分组第二种分组(方便记忆):

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

 

本文转载自:https://www.cnblogs.com/yyyg/p/5498803.html

共有 人打赏支持
Airship
粉丝 41
博文 965
码字总数 20029
作品 0
南京
高级程序员
私信 提问
Python 正则表达式(字符)详解

Python正则表达式 — 简介  其实正则表达式这种技术,源于一个很简单的问题:    如何通过变成使得计算机具有在文本中检索某种模式的能力?   而正则表达式为通过编程实现高级的文本模式...

jamesjoshuasss
2017/03/01
0
0
Python3爬虫视频学习教程

大家好哈,现在呢静觅博客已经两年多啦,可能大家过来更多看到的是爬虫方面的博文,首先非常感谢大家的支持,希望我的博文对大家有帮助! 最近,主要的任务就是开发性感美女图片大全,使用p...

yangjiyue0520
2017/11/18
0
0
Python爬虫入门,如何使用正则表达式

按照之前的课程安排,此次课程应该讲python基础第十六讲爬虫,但是如果正则一笔带过大家可能还是没办法写出自己想要实现的结果,所以把正则匹配详解提前学习一下,此次课程的学习路线会翻译官...

程序员补给栈
01/28
0
0
对于Python字符编码的理解

首先请理解这句话:对于Python的内建函数encode()和decode()来说,它都是针对字符在内存中(用终端所处理字符的内存表示)介于Unicode与指定编码之间的转换。 示例一: s = '汉字' # shell终...

Iuranus
2014/05/10
0
0
python测试开发自动化测试数据分析人工智能自学每周一练-2018-07

python每周一练 每周五发布python需求,所有需求都来自实际企业。下周五发布参考答案。 python测试开发自动化测试数据分析人工智能自学每周一练-2018-06 2018-07-06 使用python3 smtplib通过...

Python测试开发人工智能
2018/07/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何开发一款以太坊(安卓)钱包系列2 - 导入账号及账号管理

这是如何开发一款以太坊(安卓)钱包系列第2篇,如何导入账号。有时用户可能已经有一个账号,这篇文章接来介绍下,如何实现导入用户已经存在的账号。 导入账号预备知识 从用户需求上来讲,导...

Tiny熊
今天
2
0
intellJ IDEA搭建java+selenium自动化环境(maven,selenium,testng)

1.安装jdk1.8; 2.安装intellJ; 3.安装maven; 3.1 如果是单前用户,配置用户环境变量即可,如果是多用户,则需配置系统环境变量,变量名为MAVEN_HOME,赋值D:\Application\maven,往path中...

不最醉不龟归
今天
4
0
聊聊ShenandoahGC的Brooks Pointers

序 本文主要研究一下ShenandoahGC的Brooks Pointers Shenandoah Shenandoah面向low-pause-time的垃圾收集器,它的GC cycle主要有 Snapshot-at-the-beginning concurrent mark包括Init Mark(P......

go4it
昨天
4
0
Makefile通用编写规则

#简单实用的Makefile模板: objs := a.o b.o test:$(objs) gcc -o test $^ # .a.o.d .b.o.d dep_files := $(foreach f,$(objs),.$(f).d) dep_files := $(wildcard $(dep_files)) ifneq ($(d......

shzwork
昨天
3
0
《万历十五年》的读后感作文4000字

《万历十五年》的读后感作文4000字: 万历十五年,即1587年,距今已过去432年。在明朝276的历史中,这一年很平淡,并没有什么特别之处。黄仁宇的《万历十五年》一书,有别于其他的历史叙述方...

原创小博客
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部