Python|正则表达式一篇搞定,及《正则表达式应知应会》下载链接

小智雅汇 2018-08-05 10:37

正则表达式是用来描述字符串规则的字符串,也就是用字符串(描述规则)来匹配字符串(待匹配的字符串),并返回匹配的结果(匹配成功的逻辑值或匹配到的子字符串)。

已保存

正则表达式是处理字符串的强大工具。

绝大多数编程语言都支持正则表达式,语法也是大同小异。

一般的匹配流程:

正则表达式文本←逐个字符匹配→需要匹配的文本→返回匹配结果;

在正则表达式中,有特殊用途表示一定规则的字符称为元字符(.\()[]{}+-!$^*),元字符以外的字符称为普通字符。普通字符串可以匹配自身,如src,即匹配src。如果是要将元字符当做普通字符使用,前面要加\字符,字符\后跟的元字符转变为普通字符字符。(数字或部分字母前加\表示特殊的元字符,用来表示控制字符,如\n表示换行,或特殊的类别,如\d表示数字类)

1 基本元字符

1.1 . 匹配除\r\n之外的任何单个字符;

1.2 | 逻辑或操作,前后可以是单个字符也可以是逻辑分组(具体见后面的介绍);

1.3 \ 转义字符(包括两方法,后面跟元字符,将元字符转为普通字符,后面跟字母,表示特殊的元字符。)

如果使用r前缀,就可以不用考虑转义的问题了,它标识了字符串是一个原生字符串,如:r'D:\test\test.txt'

2 字符集合(单个匹配)

[] 表示一个字符集合,匹配包含的每一个字符(只匹配需要匹配的字符串中的一个);

2.1 [] 内列举元素,如[abcdewxyz],可以匹配一个其中的任一字符;

2.2 []内使用连字符-,因为在我们使用的字符集如ASCII、Unicode中的数字、大写字母、小写字母、汉字都是连续编码的:

对于字符集中连续编码的字符可以使用连字符,如用[0-9]表示0-9十个数字的列举,可以匹配其中的任一个数字。如[abcdewxyz]可以写成[a-ew-z]

2.3 []内部的最前面使用^,如[^xyz],表示字符集取反,匹配不包含列举的字符以外的任一字符;

在某些情况下,普通字符组与”|”匹配效果是一样的,如[abc]与a|b|c

3 预定义字符集(可以写在字符集[…]中,由\加一个字母或数字构成)

3.1 \d匹配一个数字,等价于[0-9],如a\dc可以匹配a0c或a1c或a9c等;

3.2 \D匹配一个非数字字符,等价于[^0-9],如a\Dc可以匹配abc或adc等,只要中间字符不是数字即可;

3.3 \s匹配任何一个不可见字符,包括换页符、换行符、回车符、制表符、垂直制表符及空格等。等价于[ \f\n\r\t\v]

3.4 \S匹配任何一个可见字符,等价于[^ \f\n\r\t\v]

3.5 \w匹配任何一个包括下划线在内的大小写字母、数字、汉字等字符(非标点、非运算符),这里的单词字符使用Unicode字符集;

3.6 \W匹配任何一个非字母、数字、汉字字符(标点、运算符);

3.7 \x匹配一个十六进制数字,在字符集ASCII或Unicode中表示特定的字符;

3.8 \num匹配一个八进制数字(num表示1-4位的数字);

3.9 \U匹配Unicode字符集中的一个字符;

import re

str = 'abc?.^ijk<>=*xyz'

#restr = re.compile(r'\W*') # 编译的话在重复使用时提高速度

restr = '\w+'

print(re.search(restr,str)[0])

restr = '\W+'

print(re.search(restr,str)[0])

'''

abc

?.^

'''

4 数量词(重复匹配,用在字符、[…]、(…)之后)

前面的元字符都只能匹配单个字符,如果要将正则表达式描述的单个字符匹配多次,则需要在此元字符后加上数量词,表示匹配前面字符(单个)的次数(前面提到的匹配都是匹配单个字符)。

4.1 *表示匹配前面字符任意次(0次或多次);

4.2 +表示匹配前面字符一次或多次;

4.3 ?表示匹配前面字符零次或一次;

4.4 使用{}表示具体的次数或次数区间;

4.4.1 {n},n是一个非负整数,匹配确定的n次;

4.4.2 {n,},表示至少匹配n次;

4.4.3 {n,m},表示匹配n-m次;

4.5 数量词的贪婪模式与非贪婪模式(防止过度匹配)

Python里数量词(*、+、{n,})默认总是尝试匹配尽可能多的字符,也就是使用贪婪模式。非贪婪模式(在数量词后使用问号?)则相反,总是尝试匹配尽可能少的字符。

import re

str ="……

contents1
contents2
……"

res = r'

.*
' # 默认的贪婪模式

print(re.findall(res,str)[0])

res = r'

.*?
' # 使用非贪婪模式

print(re.findall(res,str)[0])

'''

contents1
contents2

contents1

'''

数量词(*、+、{n,})后面再跟字符,后面的字符相当于边界限定,如 r'

.*
',如果后面的
有多个,则会贪婪匹配到最后的,但如果在数量词(*、+、{n,})后加问号?,则是非贪婪匹配,如 r'
.*>
',匹配到第一个则停止。

4.6 在正则表达式中,可以使用()对其分组(可以嵌套),()内的字符串称为子表达式,可以做为一个整体考虑,后面如果跟数量词,表示将()内的整体匹配规定的次数。

5 边界(位置)匹配(不消耗待匹配字符串中的字符)

5.1 字符串边界

5.1.1 ^匹配输入字符串的开始位置;如果设置RegExp对象的Mutliline属性,^也匹配\n或\r之后的位置;

5.1.2 $匹配输入字符串的结束位置;如果设置RegExp对象的Mutliline属性,^也匹配\n或\r之前的位置;

5.1.3 \A仅匹配字符串开头;

5.1.4 \Z仅匹配字符串末尾;

5.2 英文单词边界

5.2.1 \b匹配单词边界,如er\b匹配never go的er,不匹配verb的er;

5.2.2 \B匹配非单词边界,与\b相反;

6 逻辑分组与回溯引用

使用小括号()可将正则表达式分组(group),并且将匹配这个表达式的字符串保存到一个临时区域(最多9个),它们可以在正则表达式中用/1到/9的符号引用。(在dreamweaverk ,还可以用$1到$9的符号跨模式引用,即在查找中使用有逻辑分组的正则表达式,在替换在使用$1的回溯引用。)

import re

str = 'aabcabc123b'

restr = "(abc){2}"

print(re.search(restr,str)[0])

str = 'aabcabc123b'

restr = "(?Pabc){2}"

print(re.search(restr,str)[0])

'''

abcabc

abcabc

'''

逻辑分组与逻辑或及分组嵌套:

import re

str="ping 12.159.46.222"

res = r'(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}'

s = re.search(res,str)[0]

print(s)

'''12.159.46.'''

分组的回溯引用:

import re

str="this is a block of of test,several are are repeated!"

res = r'[ ]+(\w+)[ ]+\1'

s = re.search(res,str)

print(s)

res = r'[ ]+(\w+)[ ]+(\1)'

t = re.findall(res,str)

print(t)

'''

<_sre.SRE_Match object; span=(15, 21), match=' of of'>

[('of', 'of'), ('are', 'are')]

'''

使用小括号()对正则表达式进行分组形成子表达式,子表达式做为一个整体考虑.

I 可以对重复次数元字符的作用做出精确的设定和控制,

II 对|逻辑或的操作进行整体操作。

III 可以被回溯引用(相当于一个变量)

另外,子表达式在re.findall()中还可以做为列表的元组返回(后面有说明)。

7 前后查找(不作为分组,相当于字符作为边界)

语法:

str(?=str) 向前查找

(?<=str)str 向后查找

str(?!str) 负向前查找

(?

7.1 向前查找:

import re

str="http://www.forta.com/"

res = r'.+(?=:)'

s = re.search(res,str)[0]

print(s)

str2="ftp://www.forta.com/"

s = re.search(res,str2)[0]

print(s)

'''

http

ftp

'''

7.2 向前向后查找

import re

str=''

res = r'(?<=img src=").+?(?=")'

s = re.search(res,str)[0]

print(s)

'''

http://baidu.com/img/1.jpg

'''

7.3 返回内的内容

import re

str="…ccc111…ddd"

res = r'(?<=\<[Bb]ody>).*?(?=)'

s = re.search(res,str)[0]

print(s)

'''…ccc'''

7.4 边界字符也可以使用逻辑或

str = 'word2000word2007word2010word2013'

restr = 'word(?=2010|2013)'

print(re.search(restr,str))

'''<_sre.SRE_Match object; span=(16, 20), match='word'>'''

7.5 (?)内使用:而不是=时,包含后面的边界字符

import re

str = 'word2000word2007word2010word2013'

#restr = re.compile(r'word(?:2010|2013') 编译的话在重复使用时提高速度

restr = 'word(?:2010|2013)'

print(re.search(restr,str)[0])

'''word2010'''

8 在正则表达式里嵌入条件

正则表达式里的条件要用?来定义。如前面已经见过的两种特定条件:

I ?匹配前面一个字符或子表达式;

II ?=或?<=构造前后查找,匹配前面或后面的文本作为边界,但不包括边界;

正则表达式中嵌入条件建立在回溯引用子表达式和前后查找的基础之上,判断符号为“?”,具体语法为:

(?(backreference)true-regex|false-regex)

8.1 回溯引用条件

例如匹配北美电话号码格式:

import re

str="tel (123)456-7890"

res = r'(\()?\d{3}(?(1)\)|-)\d{3}-\d{4}'

s = re.search(res,str)[0]

print(s)

str2="tel 123-456-7890"

s = re.search(res,str2)[0]

print(s)

'''

(123)456-7890

123-456-7890

'''

(\()?构成回溯的条件,在成功的前提下,(?(1)\)|-)\构成回溯引用。注意在条件里,回溯引用编号不需要被转义,因此“?(1)”正确,而“?(\1)”不正确。

8.2 根据一个前后查找来进行条件处理

例如匹配美国邮政编码:\d{5}(?(?=-)-\d{4})。

嵌入条件的功能可以很好的补足正则表达式对于“if”条件的判断,从而执行相应的查找。

9 常用的正则表达式构造

9.1 URL地址

import re

str="http://forta.com/blog/others?abc"

res = r'https?://[-\w.]+(:\d)?(/([\w/_.]*)?)?'

s = re.search(res,str)[0]

print(s)

'''http://forta.com/blog/others'''

9.2 完整的URL地址

import re

str="http://forta.com/blog/others?abc"

res = r'https?://(\w*:\w*@)?[-\w.]+(:\d)?(/([\w/_.]*(\?\S+)?)?)?'

s = re.search(res,str)[0]

print(s)

'''http://forta.com/blog/others?abc'''

9.3 邮箱地址

import re

str="mail wwu@163.com"

res = r'(\w+\.)*\w+@(\w+\.)+[A-Za-z]+'

s = re.search(res,str)[0]

print(s)

'''wwu@163.com'''

9.4 HTML注释

import re

str="contents "

res = r''

s = re.search(res,str)[0]

print(s)

''''''

10 re.match方法与匹配模式

re.match()方法尝试从字符串的开始位置开始匹配一个模式。匹配不成功返回None,成功可以用group()或[0]引用匹配内容。

语法:re.match(pattern,string[,flags]

import re

str = "Hello world!"

print(re.match("world",str))

print(re.match("hello",str))

print(re.match("Hello",str)[0])

print(re.match("hello",str,re.I)[0])

'''None

None

Hello

Hello

'''

上例中的re.I表示匹配对大小写不敏感,匹配的模式还包括:

re.G 返回全部匹配的结果(如果未指定,一般只返回第一个匹配的结果);

re.L 做本地化识别匹配;

re.M 多行匹配,影响^和$;

re.S 使.匹配包括换行符在内的所有字符;

re.U 根据Unicode字符集解析字符

re.X 采用灵活格式

另外,匹配模式还可以直接写在正则表达式中,如分行匹配模式还可以写成“(?m)”(字母必须小写):

“(?m)”将会使整个模式变成分行匹配模式,该符号必须放在整个模式最前面。此时,“^”将匹配一行(而不是一个字符串)的开头,“$”将匹配一行(而不是一个字符串)的结尾。

m modifier: multi line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string)

例如:(?m)^\s\/\/.*$

将匹配:编程语言中以“//”为开头的注释(包括注释前面的任意多个空白符)。

忽略大小写的匹配:

import re

str="ABC"

res = r'(?i)abc'

s = re.search(res,str)[0]

print(s)

'''ABC'''

11 使用()分组提取子串

正则表达式中可以使用()分组,匹配结果用group()或[]获取。

import re

str="Cats are smarter than Pigs"

matchObj = re.match(r'(.*) are (.*?) .*',str,re.M|re.I) #式中的?表示非贪婪匹配

print(matchObj.group())

print(matchObj.group(1))

print(matchObj.group(2))

print(matchObj[0])

print(matchObj[1])

print(matchObj[2])

'''

Cats are smarter than Pigs

Cats

smarter

Cats are smarter than Pigs

Cats

smarter

'''

注意到group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串。

12 re.search()方法

re.search()方法尝试从字符串的任意位置开始匹配一个模式。

语法:re.search(pattern,string[,flags]

import re

str = "Hello world!"

print(re.search("world",str)[0]) # 使用正则表达式

print(str.find("world")) # 使用字符串方法

print(str.index("world"))

'''

world

6

6

'''

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

13 re.findall()方法

语法:re.findall(pattern,string[,flags]

调用findall()方法可以获得正则表达式在字符串中所有匹配(从左到右)结果的列表。

如果pattern里含有组的话,会返回匹配到的组的列表。如果pattern里有多个分组,那么各组会先组成一个元组,然后返回值将是一个元组的列表。

import re

str = '跟璇女神爱上Java'

restr = '

print(re.search(restr,str)[0])

print(re.search(restr,str)[1])

'''

http://assets.kgc.cn//upload/open/20170720/1500527506617342_thumb.jpg

'''

restr = '

s = re.findall(restr,str)

print(s[0])

print(s[0][0])

print(s[0][1])

'''

('http://assets.kgc.cn//upload/open/20170720/1500527506617342_thumb.jpg', '跟璇女神爱上Java')

http://assets.kgc.cn//upload/open/20170720/1500527506617342_thumb.jpg

跟璇女神爱上Java

'''

注意: match() 和 search() 只匹配一次(默认的是非G的匹配模式,匹配成功可以使用group()或下标[0]返回结果,不成功则返回None。如果使用分组匹配,则每一个分组都要匹配一次)。而 findall()匹配所有(返回一个列表或空表)。

14 re.finditer()方法

和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

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

import re

it = re.finditer(r"\d+","12a32bc43jf3")

for match in it:

....print(match.group())

输出结果:

12

32

43

3

15 re.sub()方法

re.sub()方法用于替换字符串中的匹配项。

语法:re.sub(pattern,repl,string,count=0)

可选参数count是模式匹配后替换的最大次数,且必须是非负整数,缺省值是0,表示替换所有的匹配。

import re

date="2018-08-03"

print(re.sub(r'-',"",date)) #使用正则表达式替换

print(date.replace('-',"")) #使用字符串方法替换

'''

20180803

20180803

'''

16 re.split()方法

re.split()方法类似字符串内置函数split(),二者的区别在于,内置函数split()以参数分割字符串,而正则表达式方法split()以正则表达式分割字符串。

语法:re.split(pattern,string[,maxsplit=0])

使用正则表达式分割有其优势所在,因为其可以描述复杂的情形,如用空格分割字符串时,可以是一个空格、多个空格同时存在,分割后不包括空格在内。

import re

str = "Words,words,words."

print(re.split('\w+',str))

print(re.split('\W+',str))

print(re.split('\W+',str,1))

print(str.split(sep=","))

'''

['', ',', ',', '.']

['Words', 'words', 'words', '']

['Words', 'words,words.']

['Words', 'words', 'words.']

'''

print(re.split(r'\s*','a b c d'))

print('a b c d'.split(" "))

'''

['a', 'b', 'c', 'd']

['a', 'b', '', 'c', '', '', 'd']

'''

17 re.compile方法

正则表达式可以直接使用,也可以预编译后再使用。预编译后再使用的优势是可以检查表达式是否合法,如果不合法会报错。另外,当重复使用正则表达式时,不需要每一次使用时都需要再次编译,可以提高运行速度。

语法:re.compile(pattern[,flags])

18 总结一下

18.1 括号在正则表达式内的使用

() 构造逻辑分组,使用在逻辑或|、数量词、前后查找、嵌入条件、回溯引用、构造匹配模式中;

[]构造字符集合;

{} 构造数量词、形成重复匹配;

18.2 问号?在正则表达式内的使用

I 做为数量词,匹配0次或1次;

II 构造前后查找;

III 构造嵌入条件;

IV 在数量词后使用,构造非贪婪匹配(防止过度匹配);

18.3 正则表达式对象的方法与字符串对象方法

应该说,正则表达式对象的方法比字符串对象方法的功能更强大,但后者使用较简单。

最好的测试和参考网站:https://regex101.com/

参考:

https://blog.csdn.net/Vanswells/article/details/80484115

内含《正则表达式必知必会》PDF文档下载链接。《正则表达式必知必会》是一本关于正则表达式使用最好的参考书之一。

《正则表达式必知必会》官网:http://www.forta.com/books/0134757068/

https://pan.baidu.com/s/1ciFoc7P_YVgs_qUWdinE3w

下载提取密码:u8fh

-End-

本页共372段,10210个字符,17766 Byte(字节)