python正则

python正则表达式

简介

正则表达式(Regluar Expressions)又称规则表达式,在代码中常简写为REs,regexes或regexp(regex patterns)。它本质上是一个小巧的、高度专用的编程语言。 通过正则表达式可以对指定的文本实现
匹配测试、字串/内容查找、子串/内容替换、字符串分割 等功能。

在Python中正则通过 re 模块实现。

常用的字符含义

元字符

.     匹配除换行符(\n)外的任意字符   #re中如果需要.能够匹配
      包括换行符的任意字符,需要加参数re.S。re.findall(patter,str,re.S)

^     匹配开始位置,多行模式下匹配每一行的开始

$     匹配结束位置,多行模式下匹配每一行的结束

*     前面一个字符出现0次到无穷次(0,无穷)   {0,}
 
+     前面一个字符出现1次到无穷次(1,无穷)   {1,}

?     前面一个字符出现0次到1次(0,1)        {0,1}

{m,n} 前面一个字符出现m次到n次,若省略n,则匹配m至无限次

|     或。匹配|左右表达式任意一个,从左到右匹配,如果|没有包
      括在()中,则它的范围是整个正则表达式

[]    字符集。对应的位置可以是字符集中任意字符。字符集中的字
      符可以逐个列出,也可以给出范围,如[abc]或[a-c]。
      [^abc]表示取反,即非abc。

()    被括起来的表达式将作为分组,从表达式左边开始没遇到一个
      分组的左括号“(”,编号+1.分组表达式作为一个整体,可以
      后接数量词。表达式中的|仅在该组中有效。

反斜杠作用

  • 反斜杠后边跟元字符去除特殊功能;(即将特殊字符转义成普通字符)

  • 反斜杠后边跟普通字符实现特殊功能;(即预定义字符)

  • 引用序号对应的分组所匹配的字符串。

\number  匹配和前面索引为number的分组捕获到的内容一样的字符串
>>> x=re.search(r'(tina)(fei)haha\2','tinafeihahafei tinafeihahatina').group()
>>> print (x)
'tinafeihahafei'

预定义字符集

\w      匹配字母数字及下划线   [a-zA-Z0-9_]
\W      匹配非字母数字下划线,即匹配特殊字符   [^a-zA-Z0-9_]
\s      匹配任意空白字符,等价于[\t\n\r\f\v]
\S      匹配任意非空字符, [^\s]
\d      匹配任意数字,等价与[0-9]
\D      匹配任意非数字  [^0-9]
\A      仅匹配字符串开头,同^
\Z      仅匹配字符串结尾,同$
\b      匹配\w和\W之间,即匹配单词边界匹配一个单词边界,也就
		是指单词和空格间的位置。例如, 'er\b' 可以配"never"
        中的 'er',但不能匹配 "verb" 中的 'er'。

\b的应用示例

>>> re.findall('\bhello','hi helo hello')
[]          #未加r,出现问题
>>> re.findall(r'\bhello','hi helo hello')
['hello']    #加上r,匹配成功    \b是特殊符号必须前面加r
>>> re.findall(r'hello\b','hi helo hello')
['hello']
>>> re.findall(r'\bhello','hi helo|hello')
['hello']
>>> re.findall(r'\bhello\b','hi helo|hello ')
['hello']
>>> re.findall(r'\bhello\b','hi helo|hello')
['hello']
>>> 

分组的用法
()被括起来的表达式将作为分组。

(?P<name>)     分组,除了原有的编号外再指定一个额外的别名name
(?P=name)      引用别名为<name>的分组匹配到字符串
(?:...)        分组的不捕获模式,计算索引时会跳过这个分组,取消优先级,相当于取消括号内分组(去掉括号)
\number        匹配和前面索引为number的分组捕获到的内容一样的字符串
(?=...)        顺序肯定环视,表示所在位置右侧能够匹配括号内正则
(?!...)        顺序否定环视,表示所在位置右侧不能匹配括号内正则
(?<=...)       逆序肯定环视,表示所在位置左侧能够匹配括号内正则
(?<!...)       逆序否定环视,表示所在位置左侧不能匹配括号内正则
(?(id/name)yes|no)     若前面指定id或name的分区匹配成功则执行yes处的正则,否则执行no处的正则

re模块中常用函数

常用函数:

函数 说明
re.compile() 编译正则表达式模式,返回一个对象的模式
re.findall() 搜索字符串,以列表类型返回全部能匹配的子串
re.finditer() 搜索字符串,返回一个匹配结果的迭代类型,每隔迭代元素是match对象
re.search() 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
re.match() 从一个字符串的开始位置起匹配正则表达式,返回match对象
re.split() 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
re.sub() 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
re.subn() 作用和sub一样, 唯一不同之处在于返回值为一个元组,(替换后的字符串,发生替换的次数)

re.compile()

编译正则表达式模式(规则),返回一个对象,可以对这个对象使用findall()等方法。常用于多次匹配同一规则的情况。匹配一次可以直接写入规则。

语法格式

re.compile(pattern,flags=0)
pattern : 编译时用的表达式字符串
flags :编译标志位,用于修改正则表达式的匹配方式。如:是否区分大小写,多行匹配等。

常用flag:

标志
含义
re.S (DOTALL)
使.(点)匹配包括换行在内的所有字符
re.I(IGNORECASE)
忽略大小写匹配
re.L(LOCALE)
做本地化识别(locale-aware)匹配,法语等
re.M(MULTILINE)
多行匹配,影响^和$
re.X(VERBOSE)
该标志通过给予更灵活的格式以便将正则表达式写得更易于理解
re.U
根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B

re.compile示例:

import re
obj=re.compile('\d{3}')
res1=obj.search('abc123eeee')
res2=obj.findall('qweqqw666###000')
print(res1.group())
print(res2)

#执行结果
123
['666', '000']

re.findall()

re.findall遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表。如果没有匹配到任何子串,返回一个空列表。

语法格式

re.findall(pattern, string, flags=0)
pattern 为正则表达式
string 为待操作字符串
flags 为所用模式

re.findall()示例:

import re
p = re.compile(r'\d+')
print(p.findall('qk1$####2mr3k4'))
#执行结果
['1', '2', '3', '4']

import re
s1 = 'so what,so cool,so bad,so good,and so on oo'
print(re.findall('\w*oo\w*',s1))
print(re.findall('(\w)*oo(\w)*',s1))   #只显示分组内内容
#执行结果
['cool', 'good', 'oo']
[('c', 'l'), ('g', 'd'), ('', '')]

re.finditer()

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

语法格式

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

参数和作用与 findall 一样,不同之处在于 findall 返回一个列表, finditer 返回一个迭代器

re.finditer()示例

import re

p = re.compile(r'\d+')
g = p.finditer('qk1$####2mr3k4')
print(g)
for i in g:
    print(i)
    print(i.group())

#执行结果
<callable_iterator object at 0x000001D595F59358>
<_sre.SRE_Match object; span=(2, 3), match='1'>
1
<_sre.SRE_Match object; span=(8, 9), match='2'>
2
<_sre.SRE_Match object; span=(11, 12), match='3'>
3
<_sre.SRE_Match object; span=(13, 14), match='4'>
4

re.search()

在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象,即匹配到一个就返回。如果字符串没有匹配,则返回None。

语法格式
re.search(pattern, string, flags=0)

re.search使用示例

import re
a = "123abc456"
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group())    #123abc456,返回整体
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

#执行结果
123abc456
123abc456
123
abc
456
############################
group(1) 列出第一个括号匹配部分
group(2) 列出第二个括号匹配部分
group(3) 列出第三个括号匹配部分

re,match()

用法和上面的search()一样,不同的是match是从字符串开头匹配,开头不匹配则不继续找,返回None,如果找到了返回一个match object对象。

语法格式

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

match使用示例

import re
a = "123abc456"
b = "com 123 on baby"
res1 = re.match('123',a)  #匹配成功返回match对象
res2 = re.match('123',b)  #匹配失败返回None
print(res1)
print(res2)

#执行结果
<_sre.SRE_Match object; span=(0, 3), match='123'>
None

re.split()

按照能够匹配的子串将string分割后返回列表。可以使用re.split来分割字符串,如:re.split(r'\s+', text);将字符串按空格分割成一个单词列表。

语法格式

re.split(pattern, string[, maxsplit])
maxsplit用于指定最大分割次数,不指定将全部分割。

import re
print(re.split('\d+','one1two222three3four4five555'))
print(re.split('\d+','one1two222three3four4five555',2)) 

#执行结果
['one', 'two', 'three', 'four', 'five', '']
['one', 'two', 'three3four4five555']

#注意re.split()如果pattern加上圆括号就会将匹配的分隔也显示
>>> re.split('\d+','hello12two3world4')
['hello', 'two', 'world', '']
>>> re.split('(\d+)','hello12two3world4')
['hello', '12', 'two', '3', 'world', '4', '']
>>> 

re.sub()

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

语法格式

re.sub(pattern, repl, string, count)
将正则表达式 pattern 匹配到的字符串替换为 repl 指定的字符串, 参数 count 用于指定最大替换次数

re.sub()使用示例

z = '1-5+6-9--9+7++50-66'
z = re.sub('\+\-|\-\+','-',z)
z = re.sub('\-\-|\+\+','+',z)
print(z)
# output> '1-5+6-9+9+7+50-66'

s = "the sum of 7 and 9 is [7+9]."

# 基本用法 将目标替换为固定字符串
print re.sub('\[7\+9\]', '16', s)
# output> the sum of 7 and 9 is 16.

# 高级用法 1 使用前面匹配的到的内容 \1 代表 pattern 中捕获到的第一个分组的内容
print re.sub('\[(7)\+(9)\]', r'\2\1', s)
# output> the sum of 7 and 9 is 97.


# 高级用法 2 使用函数型 repl 参数, 处理匹配到的 SRE_Match 对象
def replacement(m):
    p_str = m.group()
    if p_str == '7':
        return '77'
    if p_str == '9':
        return '99'
    return ''
print re.sub('\d', replacement, s)
# output> the sum of 77 and 99 is [77+99].


# 高级用法 3 使用函数型 repl 参数, 处理匹配到的 SRE_Match 对象 增加作用域 自动计算
scope = {}
example_string_1 = "the sum of 7 and 9 is [7+9]."
example_string_2 = "[name = 'Mr.Gumby']Hello,[name]"

def replacement(m):
    code = m.group(1)
    st = ''
    try:
        st = str(eval(code, scope))
    except SyntaxError:
        exec code in scope
    return st

# 解析: code='7+9'
#       str(eval(code, scope))='16'
print re.sub('\[(.+?)\]', replacement, example_string_1)
# output> the sum of 7 and 9 is 16.


# 两次替换
# 解析1: code="name = 'Mr.Gumby'"
#       eval(code)
#       raise SyntaxError
#       exec code in scope
#       在命名空间 scope 中将 "Mr.Gumby" 赋给了变量 name

# 解析2: code="name"
#       eval(name) 返回变量 name 的值 Mr.Gumby
print re.sub('\[(.+?)\]', replacement, example_string_2)
# output> Hello,Mr.Gumby

re.subn()

作用与函数 sub 一样, 唯一不同之处在于返回值为一个元组,第一个值为替换后的字符串,第二个值为发生替换的次数。

print(re.subn('[1-2]','A','123456abcdef'))
print(re.sub("g.t","have",'I get A,  I got B ,I gut C'))
print(re.subn("g.t","have",'I get A,  I got B ,I gut C'))
执行结果如下:
('AA3456abcdef', 2)
I have A,  I have B ,I have C
('I have A,  I have B ,I have C', 3)

贪婪匹配和非贪婪匹配

贪婪匹配

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

python的正则匹配默认情况是贪婪匹配:

>>> re.findall('a.*b','aabab')
['aabab']

非贪婪匹配
非贪婪匹配就是匹配尽可能少的字符,使用?来表示非贪婪匹配,比如.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

>>> re.findall('a.*?b','aabab')
['aab', 'ab']
>>>

.*?的用法

. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?</div>

就是取前面任意长度的字符,直到一个</div>出现

正则小练习

匹配IP

简单的写法:

(?:\d+[.]){3}\d+

>>> re.findall('(?:\d+[.]){3}\d+','192.168.2.11')
['192.168.2.11']
>>> 

严谨的写法:

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

>>> re.findall('(?:(?:[1-9]\d?|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:[1-9]\d?|1\d{2}|2[0-4]\d|25[0-5])','192.168.2.11')
['192.168.2.11']
>>> 

匹配邮箱

简单写法:

\S+@\S+\.\S+

严谨的写法:

[^\s@]+@[^\s@]+\.[^\s@]+    #匹配只出现一个@

具体匹配网易邮箱:
6-18个字符,只能包含字母、数字和下划线,且只能以字母开头

[a-zA-Z]\w{5,17}@(?:126|163)\.com

>>> re.findall('[a-zA-Z]\w{5,17}@(?:126|163)\.com','qwe513701@163.com')
['qwe513701@163.com']

其它常用匹配:

匹配内容 正则表达式
QQ号码 [1-9]\d{4,}
中国大陆固定电话号码 (\d{3,4}-)?\d{7,8}
中国大陆手机号码 1\d{10}
中国大陆邮政编码 \d{6}
汉字 [\u4e00-\u9fa5]+
中文及全角标点符号 [\u3000-\u301e\ufe10-\ufe19\ufe30-\ufe44\ufe50-\ufe6b\uff01-\uffee]
不含abc的单词 (?=\w+)(?!abc)
正整数 [1-9]+
负整数 -[1-9]+
非负整数(正整数+0) [1-9]+
非正整数(负整数+0) -[1-9]+
整数+0 -?[1-9]+
正浮点数 \d+.\d+
负浮点数 -\d+.\d+
浮点数 -?\d+.\d+
posted @ 2017-05-28 22:02  村口王铁匠  阅读(618)  评论(0编辑  收藏  举报