Python 入门 28 —— 正则表达式

本文介绍了Python中正则表达式的使用,包括为何使用原始字符串、正则表达式专用符号如基本字符、字符集、重复标记等,以及分组、匹配模式、编译函数和匹配对象的相关概念和应用。通过实例解析了正则表达式的各种功能,帮助读者掌握正则表达式在Python中的运用。
摘要由CSDN通过智能技术生成

正则表达式,用一个特殊模式去匹配字符串的一部分,其中依据特定的规则建立的特殊模式,就是正则表达式。

一、为什么要用原始字符串

1、Python 解释运行正则表达式的过程

正则表达式不是核心Python语言的一部分,Python 没有创建用于正则表达式的特殊语法。在Python中专门用于处理正则表达式的是 标准库 re 模块,它只是Python附带的C 语言扩展模块。也就是说,从本质上讲,正则表达式是嵌入在Python中的一种微小的、高度专业化的编程语言。

Python 解释运行正则表达式的过程是:

第一步、Python 解释运行程序读取程序文件中书写的正则表达式。

第二步、将读取的正则表达式以“字符串的形式”传给 re 模块的相关函数。

第三步、re 模块的相关函数将正则表达式编译成一系列字节码,调用 C 语言编写的匹配引擎执行。

第四步、re 模块的相关函数返回匹配引擎执行结果。

2、原始字符串

上面解释运行过程的第一步对于普通的字符是没有问题的,例如,Python 程序文件中书写的正则表达式为“abc”,那么,Python 解释运行程序传给re 模块的相关函数的也会是字符串“abc”,但是,对于反斜杠“\”时就会有一点点麻烦。

现在假设需要用正则表达式匹配字符串“ab\cd”,也就是说需要匹配一个反斜杠“\”。按照正则表达式语法,匹配一个反斜杠“\”需要用两个反斜杠,即,匹配字符串“ab\cd”所需要的正则表达式为:ab\cd,也就是, re 模块的相关函数需要接收的字符串为:ab\cd。

为了以上的匹配,在Python 程序文件中直接将正则表达式书写为“ab\cd”行不行呢?不行。因为如果写成这样,Python 解释运行程序在读取时会按照Python 的语法,将“\”解释为“\”,即,“ab\cd”在Python 解释后实际为“ab\cd”,这样Python 解释运行程序传给 re 模块的相关函数的字符串也就为:ab\cd,而不是 re 模块的相关函数所需要的:ab\cd。

所以,为了使用正则表达式去匹配字符串“ab\cd”,在Python 程序文件中需要将正则表达式书写为:ab\\cd。这样反复书写反斜杠“\”显然很容易引起混乱,为此,Python 创建了一个语法规则,也是 Python 专门为正则表达式设置了唯一的一个语法规则:原始字符串。

原始字符串,即是在一般字符串前加上字母r或R。例如:r’aaa’、R’\bxyz\n’。原始字符串中所有字符都不再进行转义。也就是说,Python 解释运行程序在碰到原始字符串时,不会再按照Python 的语法去解释它,而是原封不动地把它传给 re 模块的相关函数,这样,在原始字符串中,就可完全按照正则表达式的语法去书写匹配用的表达式了。例如,要匹配字符串“ab\cd”,直接按照正则表达式的语法写成:ab\cd,然后加r或R变成原始字符串: r“ab\cd” 或 R“ab\cd”。

在Python 程序中的正则表达式,一般都应该用原始字符串来表示。

二、正则表达式专用符号

1、基本字符

. ———— 匹配除换行符 \n 之外的任何单字符
* ———— 0次或多次,“.*” 表示任意字符串(包括空字符)
+ ———— 1次或多次,“.+” 空字符之外的任意字符串
? ———— 0次或1次,“.?” 任意单个字符(包括空字符)
| ———— 或,先级很低。例如:“ab|dc” 的意思是:ab 或 cd。

例如:

from re import findall
x='abcdabcdefgefg'

print(findall(r'.*',x))    # ['abcdabcdefgefg', '']
print(findall(r'.+',x))    # ['abcdabcdefgefg']
print(findall(r'.?',x))    # ['a','b','c','d','a','b','c','d','e','f','g','e','f','g','']
print(findall(r'da|ge',x))    # ['da', 'ge']

2、常规转义符号

\\ ———— 匹配反斜杠 \ 
\t ———— 匹配一个水平制表符
\v ———— 匹配一个垂直制表符

\f ———— 匹配一个换页符
\n ———— 匹配一个换行符
\r ———— 匹配一个回车符

例如:

from re import findall
x='abc\tDEf\vgh\fi:j\n\kL--mn\ropq'

print(findall(r'\\.+?.+?',x))    # ['\\kL']
print(findall(r'\t.+?.+?',x))    # ['\tDE']
print(findall(r'\v.+?.+?',x))    # ['\x0bgh']
print(findall(r'\f.+?.+?',x))    # ['\x0ci:']
print(findall(r'\n.+?.+?',x))    # ['\n\\k']
print(findall(r'\r.+?.+?',x))    # ['\rop']

3、字符集 [ ]

[ ] 用于匹配一组字符。在[ ]中可以单独列出字符,也可以用“起始符-终止符”来标记某一范围内的字符。 例如,[abc] 将匹配字符 a、或b、或c。[a-c] 与 [abc] 等同。

特别要注意的是,不论字符集 [ ]中内容有多少,它不表示字符串,而仅表示某一个字符,这个字符是哪个不确定,但在数量上一定是一个。

“. *、 +、?、$” 等匹配用字符在字符集中不生效。也就是说,这些匹配用字符在字符集被剥夺了特殊性,等同于普通字符。

‘^’ 放在字符集 [ ]开头,有特殊的含义,表示“非”。例如,[^xyz] 表示任意一个非x,也非y,也非z 的字符。如果’^’ 不是出现在字符集 [ ]开头,则它没有特殊含义,仅表示它本身。 例如:[x^] 将匹配“x” 或“^”。
. ^ * +

[0-9] ———— 匹配任意数字,等价于[0123456789]
[a-z] ———— 匹配任意小写字母
[A-Z] ———— 匹配任意大写字母
[a-zA-Z] ———— 匹配任意大小写字母
[a-zA-Z0-9] ———— 匹配任意大小写字母和数字

例如:

from re import findall
x='abc\tDEf\vgh*123?xyz.456'

print(findall(r'[abcdxyz]',x))    # ['a', 'b', 'c', 'x', 'y', 'z']
print(findall(r'[1-9]',x))    # ['1', '2', '3', '4', '5', '6']
print(findall(r'[^a-zA-Z0-9]',x))    # ['\t', '\x0b', '*', '?', '.']

print(findall(r'[*.]\w\w',x))    # ['*12', '.45']
print(findall(r'\w\w[*.]',x))    # ['gh*', 'yz.']

4、预定义字符集

\d ———— 匹配一个数字字符,等价于 [0-9]
\D ———— 匹配一个非数字字符,等价于 [^0-9]

\s ———— 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\n\r\t\v]
\S ———— 匹配任何非空白字符。等价于 [^\f\n\r\t\v]。

\w ———— 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'
\W ———— 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]'

例如:

from re import findall
x='abc\tDEf\vgh\fi:j\n\kL--mn\ropq123r_s_t456u"vw"789x_yz'

print(findall(r'\d.+?.+?',x))    # ['123', '456', '789']
print(findall(r'\D.+?.+?',x))    # ['abc','\tDE','f\x0bg','h\x0ci','\n\\k','L--','mn\r','opq','r_s','_t4','u"v','w"7','x_y']

print(findall(r'\s.+?.+?',x))    # ['\tDE', '\x0bgh', '\x0ci:', '\n\\k', '\rop']
print(findall(r'\S.+?.+?',x))    # ['abc','DEf','gh\x0c','i:j','\\kL','--m','n\ro','pq1','23r','_s_','t45','6u"','vw"','789','x_y']

print(findall(r'\w.+?.+?',x))    # ['abc','DEf','gh\x0c','i:j','kL-','mn\r','opq','123','r_s','_t4','56u','vw"','789','x_y']

print(findall(r'\W.+?.+?',x))    # ['\tDE', '\x0bgh', '\x0ci:', '\n\\k', '--m', '\rop', '"vw', '"78']

5、重复标记 { }

{n} ———— 次数 = n
{n,} ———— 次数 >= n
{,m} ———— 次数 <= m(包括空字符)
{n,m} ———— n <= 次数 <= m

{0,} ———— 等价于 *
{1,} ———— 等价于 +
{0,1} ———— 等价于 ?

例如:

from re import findall
x='abc\tDEf\vgh\fi:j\n\kL--mn\ropq123'

print(findall(r'\w{3}',x))    # ['abc', 'DEf', 'opq', '123']
print(findall(r'\w{3,}',x))    # ['abc', 'DEf', 'opq123']
print(findall(r'\w{,5}',x))    # ['abc','','DEf','','gh','','i','','j','','','kL','','','mn','','opq12','3','']
print(findall(r'\w{3,5}',x))    # ['abc', 'DEf', 'opq12']

6、位置标记

因为位置标记仅用于指定匹配的位置,匹配的内容无任何“宽度”,所以又被称作:零宽度断言。

^ ———— 行首。单行时,匹配字符串的开头;多行时,在每个换行符后立即匹配。
$ ———— 行尾。匹配字符串的结尾,或者后跟换行符的任何位置。
\A ———— 匹配字符串的开头,单行时,等同于 ^ 。
\Z ———— 匹配字符串结尾,单行时,等同于 $ 。
\b ———— 匹配单词边界,即单词头或单词尾。
\B ———— 匹配非单词边界。
from re import findall
x='abc\tDEf\vgh\fi:j\n\kL--mn\ropq123r_s_t456u"vw"789x_yz'

print(findall(r'^\w\w',x))    # ['ab']
print(findall(r'\w\w$',x))    # ['yz']

print(findall(r'\A\w\w',x))    # ['ab']
print(findall(r'\w\w\Z',x))    # ['yz']

print(findall(r'\b.+?.+?',x))    # ['ab','\tD','\x0bg','\x0ci',':j','kL','--','mn','\ro','"v','"7']
print(findall(r'\B.+?.+?',x))    # ['bc','Ef','h\x0c','\\k','L-','-m','n\r','pq','12','3r','_s','_t','45','6u','w"','89','x_','yz']

三、分组 ( )

在正则表达式中,可用小括号( )把部分内容括起来作为一个整体看待。每个用小括号( )括起来的部分又称作一个“分组”。当正则表达式中有分组时,系统会为每个分组按照左括号的出现的次序、从左到右、从1开始分配组号。分组0对应整个正则表达式。

import re
p = re.compile(r'(a\w)\w+(d\w)\w+(g\w)\w+(j\w)')
m = p.search('abcdefghijk')

print(m)    # match='abcdefghijk'
print(m.group())    # abcdefghijk
print(m.group(0))    # abcdefghijk
print(m.group(1))    # ab
print(m
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值