Python的正则表达式和re模块

1. 正则表达式

1.1 用处

正则表达式就是记录文本规则的代码

这句话我觉得概括的很精炼.

1.2 元字符

正则表达式里有一些元字符, 他们代表的是很多意思, 有某种特征的集合呀, 不包括某种字符的集合呀.

1.2.1 匹配字符种类的元字符

以下探讨的都是代表一个字符的元字符.

. : 就是英文字符的句号 . , 匹配除了换行符以外的任意字符。

\s: 匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等.
\S: 匹配任意不是空白符的字符.

\w: 匹配字母[a-z], [A-Z] 或数字[0-9] 或下划线_ 或汉字等.
\W: 匹配任意不是字母,数字,下划线,汉字的字符.

\d: 是匹配一位数字从0到9, 任意一个数字就行。
\D: 匹配任意非数字的字符

\b: 并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置. 这个位置和后一个字符或者前一个字符的类型不一样.
比如: \b\w, 这个\b就是匹配不是\w的字符, 并且不会匹配到这个字符; 如果是\b. , 就是匹配不是. 的字符. 就近匹配,比如\bAB 匹配A,AB\b 匹配B
\B: 就是上面反过来, 说不清, 举例来看: ‘aabbc’ 中 ‘\Ba’ 匹配的是第二个a, 因为\B代表跟后面的字符类型一样的字符, 但是不要这个\B代表的字符.

[ ]:从中挑一个字符, 注意是一个字符, 例如[fuck]就是从f, u, c, k 四个字符中挑选一个, 还有[0-9]从0到9选一个, 还有[a-d]或者[E-G], 从a, b, c, d 或者E, F, G中选一个.

| : 从前或者后, 选出取个来, 是或的意思. 例如: ‘abc|de’ 的符合匹配对象是’abc’ 或者’de’, 而不是’abc’ 或’abde’. 一定要注意.

[^ ]: 取反^配合[ ]使用, 就是[ ]内的字符以外的字符都可以, 例如: [^\d]匹配除了数字以外的任意字符, [^aeiou]匹配除了aeiou这几个字母以外的任意字符.

\A: 代表后面这个字符必须在字符串开头. 例如: ‘aba’ 用’\Aa’ 来匹配, 能匹配到第一个a, 不能匹配到第二个a; 如果用’\Ab’ 来匹配, 匹配不到.

^: 用来表示, ^后面这个字符, 必须是一行的开始, mutilline模式下, 会按行分隔符分隔成很多行, 所以会有很多行首, 在默认模式和singleline模式下, 就是一个长的字符串, 所以只有一个行首

$: $前面的这个字符, 必须是行尾, 规律同上.

一共11种.

1.2.2 控制字符数量的元字符

配合上面代表字符种类的元字符使用, 接在字符的后面, 可以代表字符重复多少次. 注意, 他们只控制前面一个字符的数量.

贪婪地匹配:
因为能匹配到的数量不确定, 所以就尽可能多的匹配, 比如’@bbb@bbb@’ 中, 用’@.+@’来匹配, 结果是’@bbb@bbb@’ .

* :前边的内容可以连续重复使用任意次以使整个表达式得到匹配。.* 连在一起, 在singleline模式下就意味着任意数量的不包含换行的字符。

+: 重复一次以上

?: 重复零次或一次

{n}: 重复n次

{n,}: 重复n次或更多次, {1,} 相当于+ .

{n,m}: 重复n到m次, {0,1} 相当于? . 例如: 0\d{2}-\d{8}。这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次), 用于匹配0xx-xxxxxxxx这样的字符串.

有时候, 我们需要这些元字符的本意, 比如我就想查找?, 这是需要用\来取消这些字符的特殊意义, \?就表示?。所以要查找\本身,要用\.

正则表达式里的分枝条件指的是有几种规则,只要满足这些规则中的任意一种,都会被当成匹配,因此我们要使用 | 把这些规则分开。

懒惰地匹配:

还是上面那个例子, 如果只想要’@bbb@bbb@’ 中, 符合条件的第一个字符串, 就可以用’@.+?@’来匹配, 结果是’@bbb@’, 是左边的那个 .

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

1.2.3 匹配重复出现某一组成部分的字符串

有的时候, 我们要用到前面匹配的字符串, 但是我们是在写正则表达式, 不知道会匹配到什么, 可以用分组来解决.

(exp): 匹配exp, 并捕获文本到自动命名的组里. 然后用这些组的编号来调用, 最左边定的分组编号是1, 以此类推. 例如’(\w).*?\1’ 可以匹配, ‘afuckafuckf’ 中的’afucka’ 和’fuckf’ 这样的字符串, 注意, 因为还没有匹配, 所以分组捕获的实际字符串是什么没有确定, 只有开始具体的字符串后, 才能知道分组匹配的是什么.

(?<>exp): 匹配exp, 在<>里面可以写分组的名字,但是调用分组的时候还是要写编号, 估计这个分组只是一个注释作用吧, 也可以写成(?’name’exp). 此处要吐槽CSDN这格式是个什么玩意.

(?:exp): 有时候要用()来改变匹配的优先级, 就要写成(?:)不然优先级是改变了, 但是把不想分组的内容分了组, 给了编号, 可能会有不便.

分组0对应整个正则表达式.

实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配, 因此所有命名组的组号都大于未命名的组.

零宽断言

断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。

正向:

(?=exp): 匹配以exp结尾的字符串, 不包快这个exp, 例如’\w(?=\d)’ 代表一个\w字符, 这个字符必须以数字结尾, 如果用来匹配’1a2bcde’, 匹配到的是a.

(?<=exp): 匹配以exp开头的字符串. 上面的例子:’(?<=\d)\w’ 匹配到的是a和b

负向:
(?!exp): 匹配不能以exp结尾的字符串.

(?<!exp): 匹配不能以exp开头的字符串.

注释
(?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

1.3 一般的软件模式

这些模式用于不同的需求. 以RegExTexter.exe为例, 默认模式, 就是. 不能代表换行符的单行模式.

IgnoreCase(忽略大小写)模式:
匹配时不区分大小写。

Multiline(多行模式):
根据换行符, 把文本换行, 但是不去掉换行符.
主要作用就是, 变出了很多个行首和行尾.

Singleline(单行模式)
更改.的含义,使它与每一个字符匹配(包括换行符\n)。

2. re模块

python中有适用于正则表达式的模块, 就是re模块.

在python中有两种使用正则表达式的方法, 第一种是, 用re下的方法直接匹配, 但是需要以字符串的形式输入正则表达式; 第二种是, 把正则表达式生成一个regex对象, 然后调用这个对象的方法来匹配.

匹配的返回值可以是str或者是match对象, match对象里面包含了匹配到的字符串的内容和索引.

python也提供了几种不同的模式来匹配:

re.I
re.IGNORECASE #这个模式是忽略字母的大小写

re.M
re.MULTILINE #多行模式

re.S
re.DOTALL #这个模式下, .也可以代表换行符

re.X
re.VERBOSE #忽略大小写模式, 可以让正则表达式看起来更好看, 我觉得还是别用吧

2.1 直接匹配:

re.match(pattern, string, flags=0)
pattern是正则表达式, string是要匹配的文本, flags是选择用什么模式来匹配.

就是锚定开头, 这个正则表达式, 如果不再开头的话, 就算没找到

匹配到了, 返回match对象. 没匹配到, 什么都不返回.

import re
a = re.match('\d', '123r123')

print(a)
(<_sre.SRE_Match object; span=(0, 1), match='1'>)

re.search(pattern, string, flags=0)
同样值匹配一次, 但是没有锚定开头

re.search('[^\d]', '123r123')
<_sre.SRE_Match object; span=(3, 4), match='r'>

re.fullmatch(pattern, string, flags=0)
看整个字符串是否符合正则表达式

re.fullmatch('.*', 'r123rr123')
<_sre.SRE_Match object; span=(0, 9), match='r123rr123'>

re.findall(pattern, string, flags=0)
从整个字符串开始匹配, 找到结果, 把结果以列表的形式返回.

re.findall('[^\d]', '123r1a23')
['r', 'a']

re.finditer(pattern, string, flags=0)
也是从整个字符串里找
返回一个迭代器, 每次迭代出符合条件的match对象

a = re.finditer('[^\d]', '123r1a23')
for i in a:
    print(i)
<_sre.SRE_Match object; span=(3, 4), match='r'>
<_sre.SRE_Match object; span=(5, 6), match='a'>

re.sub(pattern, repl, string, count=0, flags=0)
在string里找到符合pattern的字符串, 把它替换成repl,
count指定替换次数, 默认是0, 就是全局替换,
返回的是替换好的字符串.

 re.sub('\d', 'fuck','1@a@3@b')
 'fuck@a@fuck@b'

re.subn(pattern, repl, string, count=0, flags=0)
跟上面那个用法一毛一样, 区别就是, 这个会返回一个二元组, 第一个元素是替换好的string, 第二个元素是替换次数.

re.subn('\d', 'fuck','1@a@-@b')
('fuck@a@-@b', 1)

(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。

re.split(pattern, string, maxsplit=0, flags=0)
把string中符合pattern的字符串都切掉, 和str.split()效果差不多, maxsplit是指定切割次数的.

re.split('\d', 'ab1cd3efg4')
['ab', 'cd', 'efg', ''] 
#注意,如果string首尾符合pattern的话,会切出一个空串.很好解决

#得到结果lst:list
if lst[0] == '':
    lst.pop(0)
if lst[-1] == '':
    lst.pop()

2.2 生成regex对象再匹配

re.compile(pattern, flags=0)
用标识符来接收, 注意, 这个对象是要在匹配之前设置好匹配模式的.

a = re.compile('[fu][ck]', flags=re.I|re.M)
print(a)
print(type(a))
re.compile('[fu][ck]', re.IGNORECASE|re.MULTILINE)
<class '_sre.SRE_Pattern'>

然后regex的方法和re的方法差不多, 后面只放目标string就行了, 但是可以指定开始和结束的位置:

regex.match(string[, pos[, endpos]])
regex.search(string[, pos[, endpos]])
regex.fullmatch(string[, pos[, endpos]])
regex.findall(string[, pos[, endpos]])
regex.finditer(string[, pos[, endpos]])
regex.sub(repl, string, count=0)
regex.subn(repl, string, count=0)
regex.split(string, maxsplit=0)

3. match对象

match.group([group1, …])
后面括号写分组的编号, 可以得到字符串形式的分组

a = re.search('(\d)', 'ab1c')
a.group(1)
'1' #注意, 这个是out的, 不是print的

match.groups(default=None)
可以把所有的分组放在一个元组里面返回来

a = re.finditer('(\d)(\d)', '1a29bbb')
for i in a :
    print(i.groups())
('2', '9')

match.groupdict(default=None)
如果给分组命名了的话, 注意python中分组要用(?P<’name’>)来命名, 这个方法可以返回一个字典.

a = re.search('(?P<'fuck'>\d)(?P<'what'>\w)', 'ab1c2b3a')
#其实名字可以不加单引号, 但是CSDN太sb了不让这么写
print(type(a))
print(a.groupdict())
<class '_sre.SRE_Match'>
{'fuck': '1', 'what': 'c'}

猜你喜欢

转载自blog.csdn.net/LittleHuang950620/article/details/82533025