Python学习笔记<正则表达式与python>

也许你会认为正则表达式这种东西很无聊,但是其实正则表达式是文本的抽象。作为一个计算机专业的人,必须要对抽象极为敏感。

python 的文本处理功能很强大,其中一点就在于其拥有一个re的module。拥有了regular expression(正则表达式)的python才是一个完整的python。

本文不光有基本的python正则表达式的使用,而且还会有一个基础的正则表达式教程方便你来复习。
目录

正则表达初级教程

什么是正则表达式(regular expression)

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

正则表达式的保留关键字

正则表达式一共有12个保留的关键字

[ ] \ ^ $ . | ? * + ( )

字符集

如果我们想表达一个字母或者数字,应该如何使用正则表达式呢?

符号 作用
. 除了换行符的任意字符
\w 数字字母下划线或者汉字
\s 空白符
\d 数字
\b 单词的结束和开头
^ 字符串的开头
$ 字符串的结束

不可显示字符
可以使用特殊字符序列来代表某些不可显示字符:

 \t代表Tab(0x09)
 \r代表回车符(0x0D) 
 \n代表换行符(0x0A) 

在windows中文本使用\r\n来结束一行。而在Unix中则是\n

自定义一个字符集

我们现在已经有很好的方法来表达一个常规字符:\w。但是如果我们仅仅是想匹配一个元音字母呢?
正确的方法是使用[]来表示

#匹配元音字母
[auioe]

匹配大写字母

[A-Z]

-操作符限定了字符集的范围是“从”A到Z。

字符的重复

如果想表达多个同样的字符,我们应该?

重复限定 作用
* 0或多次
+ 1或多次
? 1或0次
{n} n次
{n,} 至少n次
{n,m} n到m次

还有什么

正则表达式是用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。

有了上面内容的基础,你已经可以写出一些简单的正则表达式了。比如:一个不那么严格的邮箱。

stosy@sina.com
[a-zA-Z0-9]+@[a-z0-9]+\.com

我们需要匹配到那个.号,但是.已经是保留字,所以我们需要在它前面加上一个backslash\告诉正则引擎:这个.并不是关键字,而是一个字符。

更多的正则表达式教程之后再说,这里我们不再多赘述。毕竟这篇文章是为了看看python的re用法。

python的正则表达式模块——RE

以下内容主要整理自廖雪峰的个人博客python RE官方文档

可供参考的内容还有

注意

有了准备知识,我们就可以在Python中使用正则表达式了。Python提供re模块,包含所有正则表达式的功能。由于Python的字符串本身也用\转义,所以要特别注意:

s = 'ABC\\-001' # Python的字符串
# 对应的正则表达式字符串变成:
# 'ABC\-001'

因此我们强烈建议使用Python的r前缀,就不用考虑转义的问题了:

s = r'ABC\-001' # Python的字符串

 对应的正则表达式字符串不变:

# 'ABC\-001'

判断是否匹配match

先看看如何判断正则表达式是否匹配:

>>> import re
>>> re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> re.match(r'^\d{3}\-\d{3,8}$', '010 12345')
>>>

match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None。常见的判断方法就是:

test = '用户输入的字符串'
if re.match(r'正则表达式', test):
    print('ok')
else:
    print('failed')

切分字符串(在split()中大显身手)

用正则表达式切分字符串比用固定的字符更灵活,请看正常的切分代码:

>>> 'a b   c'.split(' ')
['a', 'b', '', '', 'c']

嗯,无法识别连续的空格,用正则表达式试试:

>>> re.split(r'\s+', 'a b   c')
['a', 'b', 'c']

无论多少个空格都可以正常分割。加入,试试:

>>> re.split(r'[\s\,]+', 'a,b, c  d')
['a', 'b', 'c', 'd']

再加入;试试:

>>> re.split(r'[\s\,\;]+', 'a,b;; c  d')
['a', 'b', 'c', 'd']

如果用户输入了一组标签,下次记得用正则表达式来把不规范的输入转化成正确的数组。

分组

除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。比如:

^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:

>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'

如果正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来。

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

贪婪匹配

最后需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后面的0:

>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')

由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了。

必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪匹配:

>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')

编译

当我们在Python中使用正则表达式时,re模块内部会干两件事情:

编译正则表达式,如果正则表达式的字符串本身不合法,会报错;

用编译后的正则表达式去匹配字符串。

如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:

>>> import re
# 编译:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')
编译后生成Regular Expression对象,由于该对象自己包含了正则表达式,所以调用对应的方法时不用给出正则字符串。

个案研究:匹配电话号码

800-555-1212
800 555 1212
800.555.1212
(800) 555-1212
1-800-555-1212
800-555-1212-1234
800-555-1212x1234
800-555-1212 ext. 1234
work 1-(800) 555.1212 #1234

我需要知道区号是 800,干线号是 555,电话号的其他数字为 1212。对于那些有分机号的,我需要知道分机号为 1234。

我们先采用分步走的策略。
首先看看第一个好像比较简单!

>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})$') 
>>> phonePattern.search('800-555-1212').groups()            
('800', '555', '1212')
#但是无法匹配有分机号1234情形
>>> phonePattern.search('800-555-1212-1234')                

我们尝试写一下有1234的情形

phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})-(\d+)$')

我们注意到:除了- 还有可能出线.的分割符。所以我们可以用\D来替代-。甚至可以没有分隔符,所以再加上*。

phonePattern = re.compile(r'^(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d+)$')

最后那一部分的分机号也可以消失:

#整理一下思路:开头、三个数字、分隔符、三个数字、分隔符、四个数字、分隔符、多个数字
phonePattern = re.compile(r'^(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')

我们现在已经可以匹配多数的格式了

>>> s=phonePattern.search('800 555 1212').groups()
>>> s
('800', '555', '1212', '')
>>> s=phonePattern.search('800 555 1212x1234').groups()
>>> s
('800', '555', '1212', '1234')
>>> s=phonePattern.search('800-555-1212-1234').groups()
>>> s
('800', '555', '1212', '1234')
>>> s=phonePattern.search('800-555-1212ext.1234').groups()
>>> s
('800', '555', '1212', '1234')
>>> 

但是这样
(800) 555-1212
1-800-555-1212
work 1-(800) 555.1212 #1234

在第一组数字800之前出现了些奇怪的东西。。。
我们依然没有办法识别
第一个比较容易处理,我们只需要在^之后加上一个\D
恩。。但是那个work加上1-怎么匹配呢。。

我们发现我们在处理^上出现了失误。既然我们只是想要那四部分。又何必管整个字符串呢,去匹配^也都没有意义了!

phonePatter= re.compile(r'(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d+)$')

>>> phonePattern.search('work 1-(800)-555-1212ext.1234').groups()
('800', '555', '1212', '1234')
>>> 

版权声明:本文为博主原创文章,转载请标明出处。

posted @ 2015-10-05 16:46  Fridge  阅读(243)  评论(0编辑  收藏  举报