python 优雅的使用正则表达式 ~ 2
使用正则表达式
那些基础的理论也说了不少了现在就开始 实操 ( 不知道为啥特别喜欢这个词... ) 吧 .
上一节课说过 正则表达式也是一门语言 , 他被集成到了python当中 , 并且用 re 模块为正则表达式提供了一个接口 . 正则表达式 是用C 语言写的 , 所以效率很高 , 我们将正则表达式编译为模式对象 , 并且用他们来进行匹配 , 这也是为了提高效率 , 后面我们会经常用到 " 模式 " , 指的就是讲正则表达式
编译正则表达式 .
正则表达式被编译为模式对象 , 该对象拥有各种方法供你操作字符串 , 如查找模式匹配或执行字符串替换 .
>>> import re >>> p = re.compile('ab*') # a 和 一到正无穷个b ┌──────────────────────────────────────────────────────────────┐ │ re.compile: (pattern, flags=0) │ │ Compile a regular expression pattern, returning a pattern ob │ │ ject. │ └──────────────────────────────────────────────────────────────┘
从上面可以看出,re.compile() 也可以接受flags参数,用于开启各种特殊功能和语法变化 .
1 >>> p = re.compile('ab*',re.IGNORECASE)
正则表达式作为一个字符串参数传给了 re.compile(). 由于正则表达式并不是 python的核心部分 , 因此没有为他提供特殊的语法支持 , 所以正则表达式只能 以字符串的形式表示 . (有些应用根本就不需要正则表达式 , 所以python社区的小伙伴们认为没有必要将其纳入python的核心 ) 相反 , re 模块仅仅是作为 C 的扩展模块包含在python中 , 就像socket模块和zlib模块
正因为 \ 的强大 , 和功能的多 , 所以 \ 也有很多麻烦的地方 .
匹配字符 | 匹配阶段 | ||||||
\select | 需要匹配的字符串 | ||||||
\\select | 正则表达式需要用\\来表示\ | ||||||
\\\\select | 不巧python也需要用\\来表示\ | ||||||
1*2*2 所以就用了四个\ |
简言之为了匹配反斜杠这个字符我们需要在字符串中使用四个反斜杠才行 , 这在正则表达式中本来 \ 久用的频繁 , 这样就更多了 . 吓人 !
有问题就有接近越方案 , 方法就是用原始字符串来表示正则表达式 . ( r' xxx ' Do you Remember ?)
正则字符串 | 原始字符串 | ||||||
"ab*" | r"ab*" | ||||||
"\\\\select" | r"\\select" | ||||||
"\\w+\\s+\\1" | r"\w+\s+\1" | ||||||
python也需要用\\来表示\ 哦 |
所以使用正则表达式 , 的时候 强烈建议使用 原始字符串 来表达正则表达式
实现匹配
当你将正则表达式编译之后 (compile) , 你就回得带一个模式对象 . 该模式对象拥有很多的方法和属性 , 列举几个 .
方法 | 功能 | ||||||
match() | 判断一个正则表达式是否从开始匹配出匹配一个字符串 | ||||||
search() | 遍历字符串,找到正则表达式 | ||||||
findall() | 遍历字符串,找到正则表达式可以匹配的所有位置,并以列表返回 | ||||||
finditer() | 遍历字符串,找到正则表达式的所有位置,并以迭代器的形式返回 |
>>> import re >>> p = re.compile(r'[a-z]+') >>> p re.compile('[a-z]+')
我们尝试用正则表达式 [a-z] 来匹配各种字符串 .
例如
>>> p.match("") >>> print(p.match("")) None
因为 " + " 代表一个或者多个 , 所以字符串不能被匹配 . 因此 match() 返回 None
>>> m = p.match("fishc.com") >>> m <_sre.SRE_Match object; span=(0, 5), match='fishc'>
接下来让我们看看匹配对象里面都有那些信息吧 .
方法 | 功能 | ||||||
group() | 返回匹配的字符串 | ||||||
start() | 返回匹配的开始位置 | ||||||
end() | 返回匹配的结束位置 | ||||||
span() | 返回元祖表示的匹配位置(开始,结束) |
在实际应用中 , 最常用的方式是将匹配对象放在一个局部变量当中 , 检查其返回值是否为 None .
p = re.compile( ... ) m = p.match( 'string goes here' ) if m: print('Match found: ', m.group()) else: print('No match')
下面说一下find
>>> p = re.compile('\d+') >>> p.findall('3只小甲鱼,15条腿,多出的3条在哪里?') ['3', '15', '3']
>>> iterator = p.finditer('3只小甲鱼,15条腿,还有3条去了哪里?') >>> iterator <callable_iterator object at 0x10511b588> >>> for match in iterator: print(match.span()) (0, 1) (6, 8) (13, 14)
如果列表非常的大的话 , 就是用迭代器返回 , 这样效率会高不少 .
我们上面讲的都需要创建模式对象 , 其实也不一定需要如此.
re 模块还提供了 一些全局函数 , 例如match() , search() , findall() , finditer() 等等 . 这些函数第一个参数是正则表达式字符串 , 其他参数跟模式对象同名的方法采用一样的参数 ; 返回值也一样 , 同时也是返回 None 或者 匹配对象 .
>>> print(re.match(r'From\s+', 'From_FishC.com')) None >>> re.match(r'From\s+', 'From FishC.com') <_sre.SRE_Match object; span=(0, 5), match='From '>
那么我们到底是该使用 上上面说的想编译一个模式对象 , 再调用模式对象的方法呢 ? 还是直接使用这些 模块级别的函数呢 ? 当然我们如果只是使用一两次 , 使用全局变量还是比较方便的 , 但是我们如果实在循环中使用正则表达式(需要用的多) , 还是应该先 , 编译 然后开始匹配 . (因为 编译可以在循环外面进行么 , 这样就显得 比较节省时间了 . )
编译标识 (flag) (pattern, string, flags=0)
编译标识可以让你修改正则表达式的工作方式 . 在re 模块下面 , 编译标识均有两个名字 : 完整的名字和简写 . 例如 IGNORECASE的简写是 I
标识 | 含义 | ||||||
ASCLL , A | 使得转义符号如 \w , \b , \s ,\d 只能匹配ASCLL字符 | ||||||
DOTALL , S | 使得 . 能匹配任何符号 , 包括换行符 . | ||||||
IGNORECASE , I | 匹配的时候不群分大小写 . | ||||||
LOCALE , L | 支持当前的语言 (区域) 设置 | ||||||
MULTILINE , M | 多行匹配 , 会影响到 ^ 和 $ | ||||||
VERBOSE , X | 启用详细的正则表达式 |