python3.5学习笔记(第六章)

时间:2022-06-24 03:33:42

本章内容:

  正则表达式详解(re模块)

 

1、不使用正则表达式来查找文本的内容

  要求从一个字符串中查找电话号码,并判断是否匹配制定的模式,如:555-555-5555。传统的查找方法如下:

def isPhoneNumber(text): if len(text) != 12: return False for i in range(0,3): if not text[i].isdecimal(): return False if text[3] !='-': return False for i in range(4,7): if not text[i].isdecimal(): return False if text[7] !='-': return False for i in range(8,12): if not text[i].isdecimal(): return False return True message = "Call me at 415-232-2354 tomorrow. 415-234-2545 is my office."
for i in range(len(message)): chunk = message[i:i+12] if isPhoneNumber(chunk): print('Phone number found:'+chunk) print('Done') >>> Phone number found:415-232-2354 Phone number found:415-234-2545 Done

  解析:在for循环的每一次迭代中,取自message的一段新的12个字符被赋值给变量chunk,将chunk传递给isPhoneNumber(),看是否符合电话号码的模式,如果符合就打印出这段文本。最终该循环遍历整个字符串。

 

2、用正则表达式查找文本模式

  正则表达式,简称regex,是文本模式的描述方法。比如 \d\d\d-\d\d\d-\d\d\d\d 可以匹配 3个数字,一个短线,3个数字,一个短线,4个数字,也就是上面说的电话号码的格式。

  在一个模式后面加上花括号包围的数字,比如 {3} ,就是说匹配这个模式3次。所以上面的 \d\d\d-\d\d\d-\d\d\d\d  可以表示为 \d{3}-\d{3}-\d{4} 。

  2.1 创建正则表达式对象

  python中的所有正则表达式的函数都在re模块中,使用前要先导入该模块。

import re

  向 re.compile() 传入一个字符串,表示正则表达式,它将返回一个Regex模式对象。

phone_number_regex = re.compile(r'\d{3}-\d{3}-\d{4}')

  2.2 匹配Regex对象

  Regex对象的search()方法查找传入的字符串,寻找该正则表达式的所有匹配。如果没有找到,则返回None;如果找到了,则返回一个Match对象。Match对象有一个group()方法,它返回被查找字符串中实际匹配的文本。

import re #正则表达式返回全部文本
phone_number_regex = re.compile(r'\d{3}-\d{3}-\d{4}')  #{3}表示这个表达式匹配3次
mo = phone_number_regex.search('My number is 515-555-5555') print('Phone number found:'+ mo.group()) #mo是一个Match对象,需要调用group()方法才会打印实际匹配到的文本 >>> Phone number found:515-555-5555

   2.3 正则表达式匹配小结

  (1)用import re 导入正则表达式模块。

  (2)用re.compile() 函数创建一个Regex对象(要使用原始字符串)。

  (3)向Regex对象的search()方法传入想要查找的字符串,会返回一个Match对象。

  (4)调用Match对象的group()方法,返回实际匹配文本的字符串。

 

3、用正则表达式匹配更多的模式

  3.1 利用括号分组

  添加括号将在正则表达式中创建分组没然后可以利用group方法从一个分组中获取匹配的文本。

  在正则表达式中,第一对括号是第一组,第二对括号是第二组。向group传入参数1或2,就可以取得匹配到的文本中的不同部分(分组),向group传入参数0或者不传入参数,将会取得匹配到的文本的全部内容。

import re phone_number_regex = re.compile(r'(\d{3})-(\d{3}-\d{4})') mo = phone_number_regex.search('My number is 515-555-5555') print('Phone number found:'+ mo.group(1)) >>>Phone number found:515

  如果想要一次获得全部的分组,可以使用groups方法

import re phone_number_regex = re.compile(r'(\d{3})-(\d{3}-\d{4})') mo = phone_number_regex.search('My number is 515-555-5555') g_1 ,g_2 = mo.groups() print('g_1:',g_1) print('g_2:',g_2) >>> g_1: 515 g_2: 555-5555

  因为括号在正则表达式中默认用于分组,所以想要匹配真正的括号时需要对括号进行转义(两边都要转义)

import re phone_number_regex = re.compile(r'(\(\d{3}\)) (\d{3}-\d{4})') mo = phone_number_regex.search('My number is (515) 555-5555') print('Phone number found:'+ mo.group(1)) >>> Phone number found:(515)

  3.2 用管道匹配多个分组

  希望匹配多个表达式中的一个时,可以使用管道符 ‘|’ ,如果希望匹配的文本都出现在了被查找的字符串中,那么第一次出现的匹配文本将会作为Match对象被返回。 

import re hero_regex = re.compile(r'Batman|Tina Fey') mo = hero_regex.search('Batman and Tina Fey') print(mo.group()) >>> Batman

  也可以通过指定前缀和管道符组合,实现多个表达式的匹配。

import re bat_regex = re.compile(r'Bat(man|mobile|copter|bat)') #Bat是前缀,与括号中内容进行组合。 mo1 = bat_regex.search('Batmobile lost a wheel') print(mo1.group())  #返回完全匹配的文本
print(mo1.group(1))   #返回括号分组内匹配的文本
>>> Batmobile mobile

  3.3 用问号实现可选匹配

  字符 ?表示它前面的表达式或分组在这个模式中是可选的。但是?只匹配零次或一次。

import re bat_Regex = re.compile(r'Bat(wo)?man') mo_1 = bat_Regex.search('The Adventures of Batman') mo_2 = bat_Regex.search('The Adventures of Batwoman') mo_3 = bat_Regex.search('The Adventures of Batwowoman') print(mo_1.group()) print(mo_2.group()) print(mo_3) >>> Batman Batwoman None

  3.4 用星号 * 匹配零次或多次

  星号 * 的匹配方式与 ?有所不同,星号 * 可以匹配多次即只要存在就可以匹配。

import re bat_Regex = re.compile(r'Bat(wo)*man') mo_1 = bat_Regex.search('The Adventures of Batwowowoman') print(mo_1.group()) >>> Batwowowoman

  3.5 用加号 + 匹配一次或多次(至少匹配到一次)

import re bat_Regex = re.compile(r'Bat(wo)+man') mo_1 = bat_Regex.search('The Adventures of Batman') mo_2 = bat_Regex.search('The Adventures of Batwoman') mo_3 = bat_Regex.search('The Adventures of Batwowoman') print(mo_1) print(mo_2.group()) print(mo_3.group()) >>> None Batwoman Batwowoman

  3.6 用花括号匹配特定的次数

  如果想要一个分组重复特定次数,就在正则表达式中该分组的后面,跟上一个花括号,括号中的数字表示重复的次数。如

phone_number_regex = re.compile(r'\d{3}-\d{3}-\d{4}') 

  除了制定一个数字,还可以制定一个范围,即在花括号后面写一个最小值a,一个最大值b,如{a,b}。

  正则表达式{Ha}{3,5} 将匹配 ‘HaHaHa’、‘HaHaHaHa’ 和 ‘HaHaHaHaHa’ 。也可以省略第一个或者第二个数字,不限定最小值或最大值。

import re ha_regex_1 = re.compile(r"(ha){3}") m_1 = ha_regex_1.search('hahahaha') print(m_1.group()) ha_regex_2 = re.compile(r'(ha){3,5}')  #匹配3-5次,返回尽可能多的文本(贪心匹配)
m_2 = ha_regex_2.search('hahahahahaha') print(m_2.group()) >>> hahaha hahahahaha

 

4、贪心和非贪心匹配

  python的正则表达式默认是在有多种匹配结果的情况下,尽可能匹配最长的字符串。如果需要尽可能匹配最短的字符串,在花括号后面添加一个问号 ? 即可

import re ha_regex_1 = re.compile(r'(ha){3,5}')  #匹配3-5次,返回尽可能多的文本(贪心匹配)
m_1 = ha_regex_1.search('hahahahahaha') print(m_1.group()) ha_regex_2 = re.compile(r'(ha){3,5}?')  #匹配3-5次,返回尽可能多的文本(贪心匹配)
m_2 = ha_regex_2.search('hahahahahaha') print(m_2.group()) >>> hahahahaha hahaha

  注意: 问号在python中有两种含义:一种是表示匹配可选分组,另一种是声明非贪心匹配,要注意区分。

 

5、findall() 方法

  search方法将返回被查找字符串中第一次匹配到的文本,而findall方法将返回被查找字符串中的匹配到的所有文本,findall返回的不是Match对象,而是一个字符串列表。

import re phone_numbre_regex = re.compile(r'\d{3}-\d{3}-\d{4}') phone_number = phone_numbre_regex.findall('Cell:555-555-5555 Work:888-888-8888') print(phone_number) >>> ['555-555-5555', '888-888-8888']

  如果正则表达式中有分组,则返回元组的列表。

import re phone_numbre_regex = re.compile(r'(\d{3})-(\d{3}-\d{4})') phone_number = phone_numbre_regex.findall('Cell:555-555-5555 Work:888-888-8888') print(phone_number) >>> [('555', '555-5555'), ('888', '888-8888')]

 

 

 6、字符分类

  常用字符分类的缩写代码

缩写字符分类

表示
\d 0-9的任何数字
\D 除0-9数字以外的任何字符
\w 任何字母、数字、下划线字符(可以认为是匹配‘单词’字符)
\W 除字母、数字和下划线以外的任何字符
\s 空格、制表符或换行符(可以认为是匹配‘空白’)字符
\S 除空格、制表符和换行符之外的任何字符

 

7、建立自己的字符分类

  可以用方括号 [ ] 建立自己的字符分类,比如[aeiouAEIOU]将匹配所有的元音字母,不论大小写。

import re vowelregex = re.compile(r'[aeiouAEIOU]') str_1 = vowelregex.findall('hsoaiejdpoaJEP WGEPAEF JFOPAWJE[oeofjgjop jfgawpe') print(str_1)

 

  也可以使用短横线表示字母或者数字的范围,比如字符分类[0-9a-zA-Z]将匹配所有的数字和大小写字母。

import re vowelregex = re.compile(r'[0-9a-zA-Z]') str_1 = vowelregex.findall('hlsijrg894w4t23\.wsew213^&*%^&$79832hiu we') print(str_1)

 

  注意在方括号中,普通的正则表达式符号均代表原本的意义,不需要进行转义。

  通过在字符分类的左方括号右边添加一个插入字符 ^ ,就可以得到“非字符类”,也就是说正则表达式将匹配不在这个字符分类中的字符。

import re vowelregex = re.compile(r'[^aeiouAEIOU]') str_1 = vowelregex.findall('hsoaiej WGEPAEF JFJE[oeoop jfgawpe') print(str_1)

 

 

8、插入字符和美元字符

  可以在正则表达式的开始处使用插入字符^,表示匹配必须发生在被查找文本的开始处;

import re begin = re.compile(r'^hello') answer_1 = begin.search('hello world') answer_2 = begin.search('world hello') print(answer_1.group()) print(answer_2) >>> hello None

 

 

  同样,也可以在正则表达式的末尾添加一个美元字符$,表示该字符串必须以这个正则表达式的模式结束。

import re begin = re.compile(r'hello$') answer_1 = begin.search('hello world') answer_2 = begin.search('world hello') print(answer_1) print(answer_2.group()) >>> None hello

 

  可以同时使用插入符和美元字符,表示整个字符串必须匹配该模式,而不是只匹配其中的子集。

import re begin = re.compile(r'^hello$') answer_1 = begin.search('hello') answer_2 = begin.search('world hello') print(answer_1.group()) print(answer_2) >>> hello None

 

 

9、通配字符

  句点 . 被称为“通配符” 。它匹配除换行符之外的所有字符。但是句点只能匹配一个字符,比如r'.s' 只能匹配到 ‘us’,而不能匹配‘yours’。

import re begin = re.compile(r'.') answer_1 = begin.search('hello') print(answer_1.group()) >>> h

 

  9.1 用点-星(.*)匹配所有的字符串,点-星表示任意文本。

import re begin = re.compile(r'.*') answer_1 = begin.search('hello') print(answer_1.group()) >>> hello

 

  9.2 用句点字符匹配换行

  如果想要用点-星匹配包括换行符在内的所有字符,可以通过给compile传入第二个参数re.DOTALL实现。

import re begin = re.compile(r'.*',re.DOTALL) answer_1 = begin.search('hello \nworld') print(answer_1.group()) >>> hello world

 

 

10、正则表达式常用符号

? 匹配零次或一次前面的分组
* 匹配零次或多次前面的分组
+ 匹配一次或多次前面的分组
{n} 匹配n次前面的分组
{n,} 匹配n次或更多次前面的分组
{,m} 匹配零次到m次前面的分组
{n,m} 匹配至少n次,至多m次前面的分组
{n,m}?或*?或+? 对前面的分组进行非贪心匹配
^spam 字符串必须以spam开始
spam$ 字符串必须以spam结束
. 匹配除换行符之外的所有字符
\d,\w,\s 分别匹配数字、字母和空格
\D,\W,\S 分别匹配除数字、字母和空格之外的所有字符
[abc] 匹配方括号内的任意字符
[^abc] 匹配不在方括号内的任意字符

 

11、不区分大小写的匹配

  一般来说,正则表达式是区分大小写的,如果想要正则表达式不区分大小写,可以向re.compile()中传入re.I作为第二个参数。

import re begin = re.compile(r'[a-z]*',re.I) answer_1 = begin.search('ahisdhfliNILSHILHAI') print(answer_1.group()) >>> ahisdhfliNILSHILHAI

 

 

12、用sub()方法替换字符串

  regex对象的sub方法需要传入两个参数,第一个参数是一个字符串,用于取代发现的匹配。第二个参数是一个字符串,是用正则表达式匹配的内容。sub方法返回替换完成后的字符串。

import re name_regex = re.compile(r'world') answer_1= name_regex.sub('小姐姐','hello world') print(answer_1) >>> hello 小姐姐

 

 

13、管理复杂的正则表达式

  通常正则表达式写成一段会很难阅读,所以可以将正则表达式写成多行字符串的形式,用三个引号括起来,同时向re.compile()中传入参数re.VERBOSE 告诉compile 忽略表达式中的空白符和注释。

import re name_regex = re.compile(r''' (\d{3}) #匹配三个数字 (.{3}) #匹配除换行符之外的任意内容三次 (\w{3}) #匹配三个字母 ''',re.VERBOSE) answer_1= name_regex.search('555w*whello world') print(answer_1.group()) >>> 555w*whel

 

 

14、组合使用re.I 、re.DOTALL、re.VERBOSE

  re.compile()只接受两个参数,如果希望在使用re.I忽略大小写时,同时让句点可以匹配换行符,或者可以在表达式中添加注释,可以用管道符将这些参数合并起来。

regex = re.compile(r'[a-z]',re.I|re.DOTALL|re.VERBOSE)

 

import re name_regex = re.compile(r''' ([a-z]*) #匹配任意多个字母,不区分大小写 (\n) #匹配一个换行符 ([0-9]*) # 匹配任意多个数字 ''',re.I|re.DOTALL|re.VERBOSE) answer_1= name_regex.search('abcdABCD\n8097809') print(answer_1.group()) >>> abcdABCD 8097809