码迷,mamicode.com
首页 > 编程语言 > 详细

python- re模块(正则表达式)

时间:2018-07-15 19:43:06      阅读:158      评论:0      收藏:0      [点我收藏+]

标签:部分   please   sed   arch   字符串操作   unicode   迭代器   too   c51   

讲正题之前我们先来看一个例子:https://reg.jd.com/reg/person?ReturnUrl=https%3A//www.jd.com/

这是京东的注册页面,打开页面我们就看到这些要求输入个人信息的提示。
假如我们随意的在手机号码这一栏输入一个11111111111,它会提示我们格式有误。
这个功能是怎么实现的呢?
假如现在你用python写一段代码,类似:

phone_number = input(‘please input your phone number : ‘)

你怎么判断这个phone_number是合法的呢?

根据手机号码一共11位并且是只以13、14、15、18开头的数字这些特点,我们用python写了如下代码:
技术分享图片
 1 while True:
 2     phone_number = input(please input your phone number : )
 3     if len(phone_number) == 11  4             and phone_number.isdigit() 5             and (phone_number.startswith(13)  6             or phone_number.startswith(14)  7             or phone_number.startswith(15)  8             or phone_number.startswith(18)):
 9         print(是合法的手机号码)
10     else:
11         print(不是合法的手机号码)
12 
13 判断手机号码是否合法1
判断手机号码是否合法1
技术分享图片
1 import re
2 phone_number = input(please input your phone number : )
3 if re.match(^(13|14|15|18)[0-9]{9}$,phone_number):
4         print(是合法的手机号码)
5 else:
6         print(不是合法的手机号码)
正则写法

对比上面的两种写法,此时此刻,我要问你你喜欢哪种方法呀?你肯定还是会说第一种,为什么呢?因为第一种不用学呀!
但是如果现在有一个文件,我让你从整个文件里匹配出所有的手机号码。你用python给我写个试试?
但是学了今天的技能之后,分分钟帮你搞定!

今天我们要学习python里的re模块和正则表达式,学会了这个就可以帮我们解决刚刚的疑问。正则表达式不仅在python领域,在整个编程届都占有举足轻重的地位。

不管以后你是不是去做python开发,只要你是一个程序员就应该了解正则表达式的基本使用。如果未来你要在爬虫领域发展,你就更应该好好学习这方面的知识。
但是你要知道,re模块本质上和正则表达式没有一毛钱的关系。re模块和正则表达式的关系 类似于 time模块和时间的关系
你没有学习python之前,也不知道有一个time模块,但是你已经认识时间了 12:30就表示中午十二点半(这个时间可好,一般这会儿就该下课了)。
时间有自己的格式,年月日时分秒,12个月,365天......已经成为了一种规则。你也早就牢记于心了。time模块只不过是python提供给我们的可以方便我们操作时间的一个工具而已

正则表达式和re模块

正则表达式本身也和python没有什么关系,就是匹配字符串内容的一种规则

官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式

一说规则我已经知道你很晕了,现在就让我们先来看一些实际的应用。在线测试工具 http://tool.chinaz.com/regex/

首先你要知道的是,谈到正则,就只和字符串相关了。在我给你提供的工具中,你输入的每一个字都是一个字符串。
其次,如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的。
  比如你要用"1"去匹配"1",或者用"2"去匹配"2",直接就可以匹配上。这连python的字符串操作都可以轻松做到。
那么在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。
字符组 : [字符组]
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。

  技术分享图片

字符:

技术分享图片

量词:

技术分享图片

技术分享图片

技术分享图片

 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

 技术分享图片

技术分享图片

 

分组 ()与 或 |[^]

 身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部???数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

技术分享图片

 

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成‘\\‘。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成‘\\d‘,那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r‘\d‘这个概念,此时的正则是r‘\\d‘就可以了。

 技术分享图片

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

技术分享图片

几个常用的非贪婪匹配Pattern:

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

.*?的用法

. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现

re模块下的常用方法

技术分享图片
 1 import re
 2 
 3 ret = re.findall(a, eva egon yuan)  # 返回所有满足匹配条件的结果,放在列表里
 4 print(ret) #结果 : [‘a‘, ‘a‘]
 5 
 6 ret = re.search(a, eva egon yuan).group()
 7 print(ret) #结果 : ‘a‘
 8 # 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
 9 # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
10 
11 ret = re.match(a, abc).group()  # 同search,不过尽在字符串开始处进行匹配
12 print(ret)
13 #结果 : ‘a‘
14 
15 ret = re.split([ab], abcd)  # 先按‘a‘分割得到‘‘和‘bcd‘,在对‘‘和‘bcd‘分别按‘b‘分割
16 print(ret)  # [‘‘, ‘‘, ‘cd‘]
17 
18 ret = re.sub(\d, H, eva3egon4yuan4, 1)#将数字替换成‘H‘,参数1表示只替换1个
19 print(ret) #evaHegon4yuan4
20 
21 ret = re.subn(\d, H, eva3egon4yuan4)#将数字替换成‘H‘,返回元组(替换的结果,替换了多少次)
22 print(ret)
23 
24 obj = re.compile(\d{3})  #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
25 ret = obj.search(abc123eeee) #正则表达式对象调用search,参数为待匹配的字符串
26 print(ret.group())  #结果 : 123
27 
28 import re
29 ret = re.finditer(\d, ds3sy4784a)   #finditer返回一个存放匹配结果的迭代器
30 print(ret)  # <callable_iterator object at 0x10195f940>
31 print(next(ret).group())  #查看第一个结果
32 print(next(ret).group())  #查看第二个结果
33 print([i.group() for i in ret])  #查看剩余的左右结果
View Code

注意:

1 findall的优先级查询:

1 import re
2 
3 ret = re.findall(www.(baidu|oldboy).com, www.oldboy.com)
4 print(ret)  # [‘oldboy‘]     这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
5 
6 ret = re.findall(www.(?:baidu|oldboy).com, www.oldboy.com)
7 print(ret)  # [‘www.oldboy.com‘]

2 split的优先级查询

1 ret=re.split("\d+","eva3egon4yuan")
2 print(ret) #结果 : [‘eva‘, ‘egon‘, ‘yuan‘]
3 
4 ret=re.split("(\d+)","eva3egon4yuan")
5 print(ret) #结果 : [‘eva‘, ‘3‘, ‘egon‘, ‘4‘, ‘yuan‘]
6 
7 #在匹配部分加上()之后所切出的结果是不同的,
8 #没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
9 #这个在某些需要保留匹配部分的使用过程是非常重要的。

综合练习与扩展

1、匹配标签

技术分享图片
 1 import re
 2 
 3 
 4 ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
 5 #还可以在分组中利用?<name>的形式给分组起名字
 6 #获取的匹配结果可以直接用group(‘名字‘)拿到对应的值
 7 print(ret.group(tag_name))  #结果 :h1
 8 print(ret.group())  #结果 :<h1>hello</h1>
 9 
10 ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
11 #如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
12 #获取的匹配结果可以直接用group(序号)拿到对应的值
13 print(ret.group(1))
14 print(ret.group())  #结果 :<h1>hello</h1>
View Code

2、匹配整数

技术分享图片
1 import re
2 
3 ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))")
4 print(ret) #[‘1‘, ‘2‘, ‘60‘, ‘40‘, ‘35‘, ‘5‘, ‘4‘, ‘3‘]
5 ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")
6 print(ret) #[‘1‘, ‘-2‘, ‘60‘, ‘‘, ‘5‘, ‘-4‘, ‘3‘]
7 ret.remove("")
8 print(ret) #[‘1‘, ‘-2‘, ‘60‘, ‘5‘, ‘-4‘, ‘3‘]
View Code

3、数字匹配

技术分享图片
 1 1、 匹配一段文本中的每行的邮箱
 2       http://blog.csdn.net/make164492212/article/details/51656638
 3 
 4 2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;
 5 
 6    分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
 7    一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$
 8 
 9 3、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}
10 
11 4、 匹配一个浮点数。       ^(-?\d+)(\.\d+)?$   或者  -?\d+\.?\d*
12 
13 5、 匹配汉字。             ^[\u4e00-\u9fa5]{0,}$ 
14 
15 6、 匹配出所有整数
View Code

4、爬虫练习

技术分享图片
 1 import requests
 2 
 3 import re
 4 import json
 5 
 6 def getPage(url):
 7 
 8     response=requests.get(url)
 9     return response.text
10 
11 def parsePage(s):
12     
13     com=re.compile(<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>
14                    .*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>,re.S)
15 
16     ret=com.finditer(s)
17     for i in ret:
18         yield {
19             "id":i.group("id"),
20             "title":i.group("title"),
21             "rating_num":i.group("rating_num"),
22             "comment_num":i.group("comment_num"),
23         }
24 
25 def main(num):
26 
27     url=https://movie.douban.com/top250?start=%s&filter=%num
28     response_html=getPage(url)
29     ret=parsePage(response_html)
30     print(ret)
31     f=open("move_info7","a",encoding="utf8")
32 
33     for obj in ret:
34         print(obj)
35         data=json.dumps(obj,ensure_ascii=False)
36         f.write(data+"\n")
37 
38 if __name__ == __main__:
39     count=0
40     for i in range(10):
41         main(count)
42         count+=25
View Code
技术分享图片
 1 import re
 2 import json
 3 from urllib.request import urlopen
 4 
 5 def getPage(url):
 6     response = urlopen(url)
 7     return response.read().decode(utf-8)
 8 
 9 def parsePage(s):
10     com = re.compile(
11         <div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>
12         .*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>, re.S)
13 
14     ret = com.finditer(s)
15     for i in ret:
16         yield {
17             "id": i.group("id"),
18             "title": i.group("title"),
19             "rating_num": i.group("rating_num"),
20             "comment_num": i.group("comment_num"),
21         }
22 
23 
24 def main(num):
25     url = https://movie.douban.com/top250?start=%s&filter= % num
26     response_html = getPage(url)
27     ret = parsePage(response_html)
28     print(ret)
29     f = open("move_info7", "a", encoding="utf8")
30 
31     for obj in ret:
32         print(obj)
33         data = str(obj)
34         f.write(data + "\n")
35 
36 count = 0
37 for i in range(10):
38     main(count)
39     count += 25
40 
41 简化版
简化版

flags有很多可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释

  作业:

实现能计算类似 
1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式的计算器程序

  

 

python- re模块(正则表达式)

标签:部分   please   sed   arch   字符串操作   unicode   迭代器   too   c51   

原文地址:https://www.cnblogs.com/liuye1990/p/9314123.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!