你的第一个正则表达式
正则表达式为高级的文本模式匹配、抽取、与/或文本形式的搜索和替换功能提供了基础。简单的说,正则表达式(简称为regex)是一些由字符和特殊符号组成的字符串,它们描述了模式的重复或者表述多个字符,于是正则表达式能按照某种模式匹配一系列有相似特征的字符串。——正则表达式能够匹配多个字符串,否则他就没什么意义了!
术语:“匹配”(matching):
“模式匹配”(pattern-matching),在Python术语中,主要有两种方法完成模式匹配:
1、“搜索”(searcing),即在字符串任意部分中搜索匹配的模式;
2、而“匹配”(matching)是指判断一个字符串能否从起始处全部或者部分地匹配某个模式。
常见的特殊符号和字符:
1、符号
re1|re2 匹配正则表达式re1 或者 re2
. 匹配任何字符(除了\n之外)
^ 匹配字符串其实部分
$ 匹配字符串终止部分
* 匹配0次或者多次前面出现的正则表达式
+ 匹配次或者多次前面出现的正则表达式
? 匹配0次或者1次前面出现的正则表达式
{N} 匹配N次前面出现的正则表达式
{M,N} 匹配M~N次前面出现的正则表达式
[...] 匹配来自字符集的任意单一字符
[..x-y..] 匹配x~y范围中的任意单一字符
[^...] 不匹配此字符集中出现的任何一个字符,包括某一范围的字符(如果再次字符集中出现)
(*|+|?|{})? 用于匹配上面频繁出现/重复出现符号的非贪婪版本(*、+、?、{})
(...) 匹配封闭的正则表达式,然后另存为子组
2、特殊字符
\d 匹配任何十进制数字,与[0-9]一致(\D与\d相反,表示不匹配任何非数值型的数字)
\w 匹配任何字母数字字符,与[A-Za-z0-9]相同 (与 \W 相反)
\s 匹配任何空格字符,与[\n\t\r\v\f]相同(\S与之相反)
\b 匹配任何单词边界(\B与之相反)
\N 匹配一保存的子组N
\c 逐字匹配任何特殊字符c(即,仅按照字面意义匹配,不匹配特殊含义)
\A(\Z) 匹配字符串的起始(结束)
3、扩展表示法
...
正则表达式和Python语言
re模块在python 1.5版本中被引入,用于替换regex模块和regsub模块,在python 2.5版本中被移除
re模块支持更强大而且更通用的Perl风格的正则表达式,该模块允许多个线程共享一个已编译的正则表达式对象,也支持命名子组
匹配对象以及group()和groups()方法
当处理正则表达式时,除了正则表达式对象之外,还有另一个对象类型:匹配对象
这些是成功调用 match() 或者 search()返回的对象。匹配对象有两个主要的方法:group()和groups()
使用match()方法匹配字符串
匹配成功——返回一个匹配对象
匹配失败——返回None
如果使用group()方法则能够显示那个成功的匹配。
In [2]: import re #导入re模块 In [3]: m = re.match('foo','foo') #使用match()方法匹配字符串 In [4]: if m is not None: #判断是否匹配成功 ...: m.group() ...: In [5]: m Out[5]: <_sre.SRE_Match object; span=(0, 3), match='foo'> #确认返回的匹配对象 In [6]: m.group() #使用group()方法输出匹配成功的值 Out[6]: 'foo'
在实际操作中,最好不要省去if语句块,这样可以避免AttributeError异常(None是返回的错误值,该值并没有group()属性[方法])
In [8]: m = re.match('foo','food on the table') #可以匹配成功 In [9]: m.group() Out[9]: 'foo'
匹配是从头开始,以上实例可以匹配成功
In [10]: m = re.match('foo','My food on the table') In [11]: m.group() --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-11-c6ebaf1e4dd8> in <module>() ----> 1 m.group() AttributeError: 'NoneType' object has no attribute 'group'
这里有2点需要注意:1、如果无法从头开始匹配,那么就会匹配失败; 2、如果匹配失败,直接使用group()方法就会报错,前面已经提到
In [16]: m = re.search('foo','My food on the table') In [17]: m.group() Out[17]: 'foo'
这里就可以使用search()函数来代替match()函数,但是要注意,只是搜索成功,并不表示匹配成功
匹配多个字符串
In [19]: bt = 'bat|bet|bit' In [20]: m = re.match(bt,'bat') #'bat'是一个匹配 In [21]: if m is not None: m.group() In [22]: m.group() Out[22]: 'bat'
In [23]: m = re.match(bt,'blt') In [24]: if m is not None : m.group() In [25]: m.group() #匹配'blt'失败 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-25-c6ebaf1e4dd8> in <module>() ----> 1 m.group() AttributeError: 'NoneType' object has no attribute 'group'
匹配任何单个字符
>>> anyend = '.end' >>> m = re.match(anyend,'bend') #点号 . 匹配的'b'字符,这里要注意 >>> if m is not None: m.group() ... 'bend'
>>> patt314 = '3.14' >>> pi_patt = '3\.14' >>> m = re.match(pi_patt,'3.14') #这里是精确匹配 >>> if m is not None: m.group() ... '3.14' >>> m = re.match(patt314,'3014') #点号匹配的'0' >>> if m is not None: m.group() ... '3014' >>> m = re.match(patt314,'3.14') #点号匹配的'.' >>> if m is not None: m.group() ... '3.14'
2018年3月19日
正则表达式匹配的子组
>>> m = re.match('(\w\w\w)-(\d\d\d)','abc-123') #如果不用括号将\w括起来,那么就不存在子组1,同理\d >>> if m is not None : m.group() #完整匹配 ... 'abc-123' >>> m.group(1) #子组1 'abc' >>> m.group(2) #子组2 '123' >>> m.groups() #全部子组 ('abc', '123')
group()与groups()的意义要理解清楚,下面是一个子组的特殊例子
>>> m = re.match('(a(b))','ab') >>> if m is not None : m.group() ... 'ab' >>> m.group(1) 'ab' >>> m.group(2) 'b' >>> m.groups() ('ab', 'b')
通常情况下,在正则表达式中使用原始字符串是个好主意
>>> m = re.search('^The','The end') >>> if m is not None:m.group() ... 'The' >>> m = re.search('^THe','end. The') >>> >>> if m is not None:m.group() ... >>> m = re.search(r'\bthe','bite the dog') >>> if m is not None : m.group() ... 'the' >>> m = re.search(r'\bthe','bitethe dog') >>> if m is not None: m.group() ... >>> m = re.search(r'\Bthe','bitethe dog') >>> if m is not None:m.group() ... 'the' >>>
其他4个re模块函数和正则表达式对象方法:findall()、sub()、subn()和split()
findall()查询字符串某个正则表达式模式全部的非重复出现的情况。
与match()和search()的不同之处在于,findall()总是返回一个列表,
>>> re.findall('car','car') ['car'] >>> re.findall('car','scary') ['car'] >>> re.findall('car','carry the barcardi to the car') #这里匹配了‘car’出现了3次,3次都被记录在了列表中 ['car', 'car', 'car'] >>>
一个特殊一点的例子:
>>> re.findall('@163\.com','this is my 163.com email: warlock921@163.com') ['@163.com'] >>> re.findall('@163\.com','this is my 163.com email: warlock921@163.com,your email is : xxx@163.com') ['@163.com', '@163.com']
findall()总结:对于一个成功的匹配,每个子组匹配是由 findall() 返回的结果列表中的单一元素;对于多个成功的匹配,每个子组匹配是返回一个元组中的单一元素,而且每个元组(每个元组都对应一个成功的匹配)是结果列表中的元素。
使用sub()和subn()搜索与替换
>>> re.sub('X','Mr.Smith','attn:X\n\nDear X, \n') 'attn:Mr.Smith\n\nDear Mr.Smith, \n' >>> re.subn('X','Mr.Smith','attn: X\n\nDear X,\n') ('attn: Mr.Smith\n\nDear Mr.Smith,\n', 2) >>> print(re.sub('X','Mr.Smith','attn:X\n\nDear X, \n')) attn:Mr.Smith Dear Mr.Smith, >>> re.sub('[ae]','X','abcdef') 'XbcdXf' >>> re.subn('[ae]','X','abcdef') ('XbcdXf', 2)
sub()和subn()两者几乎一样,都是将某字符串中所有匹配正则表达式的部分进行某种形式的替换。用来替换的部分通常是一个字符串,但它也可能是一个函数,该函数返回一个用来替换的字符串。
subn()和sub()一样,但subn()还返回一个表示替换的总数,替换后的字符串和表示替换总数的数字一起作为拥有两个元的元组返回。
在限定模式上使用split()分隔字符串
一个简单的示例:
re.split(':','str1:str2:str3') ['str1', 'str2', 'str3']
一个复杂的示例:
>>> DATA=( ... 'Mountain View, CA 94040', ... 'Sunnyvale, CA', ... 'Los Altos, 94023', ... 'Cupertino, 95014', ... 'Palo Alto CA', ... ) >>> for datum in DATA: ... print(re.split(', |(?= (?:\d{5}|[A-Z]{2})) ',datum)) ... ['Mountain View', 'CA', '94040'] ['Sunnyvale', 'CA'] ['Los Altos', '94023'] ['Cupertino', '95014'] ['Palo Alto', 'CA'] >>>