python基础之 正则表达式,re模块

1.正则表达式

正则表达式:是字符串的规则,只是检测字符串是否符合条件的规则而已
    1.检测某一段字符串是否符合规则
    2.将符合规则的匹配出来
re模块:是用来操作正则表达式的

2.正则表达式组成

字符组[] 一个字符组描述的是一个位置上的字符规则,但是不能从大到小来规定范围,字符组内所有的范围都是ascii来排序的,字符组更灵活一点
  [0-9] 匹配一个数字范围
  [a-z] 匹配一个小写字母
  [A-Z] 匹配一个大写字母
  [A-Za-z0-9] 匹配英文字母和大小写 左边必须是ascii最小的

  注意:遇到在字符组当中的'-'是有特殊意义的,如果想取消'-'的特殊意义,要转义一下
      例如:[1-2] 是匹配不是1就是2的数据,但是我想匹配1,-,2那?需要转义一下[1\-2]
            
元字符:匹配的一个字符的内容
  \d: 使用\d表示匹配任意从0-9的数字,使用字符组能匹配到一个固定的范围,但是\d表示的时从0-9太固定
   \w:匹配所有的数字字母下划线,标识符
   -t:匹配tab制表符
   \n:匹配换行符
   \s:匹配所有的空格换行制表符  
   \b:表示一个单词的边界,要匹配以XX结尾的单词
    反义词:
    \W:除了数字字母下划线的所有
    \D:除了数字之外的所有
    \S:除了空白之外的所有
    .:匹配换行符之外的所有字符

 非字符组[^123] 除了123的都匹配 ‘^’必须和[]组合才叫非字符组
 开始符'^' 结束符'$' 
  一个字符串只有一个开始和一个结束,^永远在一个规则的最前面,$永远在一个字符串的最末尾
  ^hello$ -->匹配一个hello,规定了这个字符串的长度,在字符串检测的时候通常加上^和$符号
  [^123] :匹配的是不以123开头的
 | 或的意思,竖杠的左边或右边是一个规则,永远把相对长的规则放在左边
 分组:()--> www\.(baidu|oldboy)\.com

量词:表示的是匹配的次数,在量词的范围内尽可能的多的匹配,叫贪婪匹配 
  {n} 表示出现n次
   {n,} 表示出至少出现n次
   {n,m}表示存出现n-m次
   ?:匹配额0次或1次
   +:匹配1次或多次
   *:匹配0次或多次
  ?和+的范围等于*的范围

   匹配电话号码:^1[3-9]\d{9}$:
匹配整数:^[1-9]\d*|0&
匹配小数:\d+\.\d+
匹配整数或者小数:\d+(\.\d+)? -->里面的()是分组的意思,?表示要不()里面的一起出现,不要就都不出现 \d+\.?\d*
匹配15位身份证号码:^[1-9]\d{14}$
匹配18位身份证号码:^[1-9]\d{16}[\dx]$
 #转义字符:在字符串外部加上一个r来不转义
贪婪匹配:尽可能的多匹配 a.*x 回溯算法
惰性匹配:尽可能的少匹配 .*? ,两个量词同时出现时表示非贪婪 a.*?x 从a开始匹配,遇到一个x就停止
      就是量词后面加上?就是惰性匹配,匹配到第一个就停止
 

re模块

import re
s='kobe123jordan46'

re.findall(pattern,string,flags=0)
  ret = re.findall('\d+',s)
  print()
  查看所有,返回一个列表
  注意:在正则条件不一样的情况下返回的内容不一样 '\d+'是贪婪的匹配所有数字组成一个字符串
re.search(pattern,string,flags=0)   ret =re.search('\d+',s)
  print(ret)
  print(ret.group())
  返回一个结果对象或结果集
  通过group()取值只取到第一个,如果没有符合条件的group就会报错,一般用if判断一下是否为空


re.match(pattern,string,flags=0)
  
ret = re.match('\s+',s)
  print(ret)
  返回一个结果对象或结果集
  使用match的话,相当于在正则表达式前面加上^,在程序里面匹配的是'^\s',是找以字符串开头的,没有的话就返回None
  使用search完全可以代替match,只要在search的表达式前面加上^就可以
  re.search('^\s+','kobe123') == re.match('\s+','kobe123')

re.sub(pattern,repl,string,count=0)
  ret = re.sub('\d+','sb',s,count=1)
  print(ret)
  count不写的话默认替换所有,返回一个字符串
  
re.subn(pattern,repl,string,count=0)
  ret = re.sun('\d+','sb',s)
  print(ret)
  默认返回一个元组,并且返回被替换了多少次
re.split(pattern,string,maxsplit=1)
  ret = re.split('\d+',s,maxsplit=1)
  print(ret)
  默认返回一个列表,不加maxsplit就全部分割

re.compile(pattern)
  par = re.compile('\d+')
  ret = par.findall(s)
  print(ret)
  对于一个经常重复使用的正则表达式,我们可以先进行一次编译,之后只要在使用这个表达式就直接拿出来使用
  就好了,这样做节省时间和及其资源,减少解析时间个转成机器码的时间
  re.compile()是一个优化函数,提升执行效率减少不必要的时间和性能的开销

re.finditer(pattern,string)
  par = re.compile('\d+')
  ret = par.finditer('kob123kobe123kobe123kobe123'*200)
  for item in ret:
    print(item.group())
  返回的是一个生成器结果集,通过group方法取出每一个数据
  看到iter应该能想到是生成器原理,通过循环每次使生成器返回一个数据,这样和使用生成器读取大文件一样,节省内存
  能够提前编译一个正则表达式,当同一个正则表达式被多次使用时,可以直接使用节省时间


分组

  s= """
    <h1>哇哈哈哈<h1>
    <h2>哈哈哈哈<h2>
    <title>科比</title>
   """

  findall()和分组:分组在findall当中会默认的优先被显示出来
    ret = re.findall('>\w+<',r'<title>科比<\title>')
    print(ret[0].strip('<>'))
    分组在findall当中会默认的优先被显示出来,如果不想优先显示出来就在分组中添加(?:正则规则)表示取消这个正则的优先显示
 
    ret = re.findall('>\w+<',r'<title>科比<\title>')
    print(ret[0].strip('<>'))

    ret = re.findall('>(\w+)<',r'<title>科比<\title>')
    print(ret) # findall永远优先显示分组中的内容

    ret = re.findall('www\.(?:baidu|nba)\.com',r'www.baidu.com')
    print(ret)

    ret = re.findall('\d+(?:\.\d+)?',r'1.23+2.34')
    print(ret)

  split()和分组:会保留被切掉的在分组中内容
    ret = re.split('\d(\d)','alex84wusir73')
    print(ret)

  search()和分组:不受到分组的影响

    ret = re.search(r'<(\w+)>(\w+)<\\(\w+)>',r'<title>科比<\title>')
    print(ret.group(0)) # 不受到分组的影响
    print(ret.group(1))
    print(ret.group(2))

分组命名

  #分组命名就是把一个要匹配的东西取一个名字,在以后调用返回的时候回方便,直接使用名字就OK了
  #如果有取值的有两个相同的名字的话可以复制为(?P=a),必须要写的分组里面,不然的不能叫分组命名了

    ret = re.search(r'<(?P<tab1>\w+)>(?P<content>\w+)<\\(\w+)>',r'<title>科比<\title>')
    print(ret.group(0)) # 不受到分组的影响
    print(ret.group('tab1')) # 不受到分组的影响
    print(ret.group('content')) # 不受到分组的影响

     特殊的需求:把这个字符串全部匹配出来<h1>wahaha</h2></h1>
    par = '<\w+>.*?</\w+>'
    ret = re.search('<(?P<tag>\w+)>.*</(?P=tag)>','<h1>wahaha</h2></h1></h3>')
    print(ret.group())

    par = '<\w+>.*?</\w+>'
    ret = re.search(r'<(\w+)>.*</\1(是使用第几个分组名字)>','<h1>wahaha</h2></h1></h3>')
    print(ret.group())

    当我们要匹配的内容混在不想匹配的内容中
    只能把不想要的也匹配出来,然后去掉不想要的就是想要的

    下列我只想取整数部分
    ret = re.findall('\d+\.\d+|(\d+)','1-2*(60+(-40.35/5)-(-4*3))') #取出来不包含40.35,但是包含"",因为findall遇到分组是优先匹配,所以匹配到小数就放弃,但是必须得返回一个元素,就返回""
    ret = re.findall('\d+\.\d+|\d+','1-2*(60+(-40.35/5)-(-4*3))')  #取出来的包含40.35
    ret.remove('')
    print(ret)

遇见分组
    findall 优先显示分组中的内容
    split 保留被切掉的分组内的内容
    search 可以通过组的索引取值
    取消分组的特殊行为(?:正则)

常见的使用正则解决问题的方式#
  1.把整个结构描述下来,对想要的进行分组#
  2.把不想要的也匹配出来,然后用手段删掉

1. 数字:^[0-9]*$
2. n位的数字:^\d{n}$
3. 至少n位的数字:^\d{n,}$
4. m-n位的数字:^\d{m,n}$
5. 零和非零开头的数字:^(0|[1-9][0-9]*)$
6. 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
7. 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$
8. 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
9. 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
10. 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
11. 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
12. 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
13. 非负整数:^\d+$ 或 ^[1-9]\d*|0$
14. 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15. 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
16. 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
17. 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
18. 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
19. 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
2.校验字符的表达式
1. 汉字:^[\u4e00-\u9fa5]{0,}$
2. 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
3. 长度为3-20的所有字符:^.{3,20}$
4. 由26个英文字母组成的字符串:^[A-Za-z]+$
5. 由26个大写英文字母组成的字符串:^[A-Z]+$
6. 由26个小写英文字母组成的字符串:^[a-z]+$
7. 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
8. 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
9. 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
10. 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
11. 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
12. 禁止输入含有~的字符:[^~\x22]+
3.特殊需求表达式
1. Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
2. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3. InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
4. 手机号码(可根据目前国内收集号扩展前两位开头号码):^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
5. 电话号码("XXX-XXXXXXX""XXXX-XXXXXXXX""XXX-XXXXXXX""XXX-XXXXXXXX""XXXXXXX""XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6. 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7.15位身份证号:^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$
8.18位身份证号:^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
9. 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
10. 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
11. 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
12. 日期格式:^\d{4}-\d{1,2}-\d{1,2}
13. 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
14. 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
15. 钱的输入格式:
(1)有四种钱的表示形式我们可以接受:"10000.00""10,000.00", 和没有 """10000""10,000":^[1-9][0-9]*$
(2)这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
(3)一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
(4)这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
(5)必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10""10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
(6)这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
(7)这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
(8)1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
16. xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
17. 中文字符的正则表达式:[\u4e00-\u9fa5]
18. 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
19. 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
20. HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
21. 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
22. 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
23. 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
24. IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
25. IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
常见的正则表达式

 other re.S和re.M

Python 的re模块内置函数几乎都有一个flags参数,以位运算的方式将多个标志位相加。其中有两个模式:单行(re.DOTALL, 或者re.S)和多行(re.MULTILINE, 或者re.M)模式。
它们初看上去不好理解,但是有时又会非常有用。这两个模式在PHP和JavaScripts里都有。

单行模式 re.DOTALL
在单行模式里,文本被强制当作单行来匹配,什么样的文本不会被当作单行?就是里面包含有换行符的文(或者是网页源码),比如:
This is the first line.\nThis is the second line.\nThis is the third line.
点号(.)能匹配所有字符,换行符例外。现在我们希望能匹配出整个字符串,当用点号(.)匹配上面这个字符串时,在换行符的地方,匹配停止。

1.在打印的时候直接打印出三行
print (a)
    This is the first line.
    This is the second line.
    This is the third line.
2.在不使用re.S的时候会按行匹配
  import re
  ret = re.findall('^This.*line',a)
  print(ret)
  在上面的例子里,即使是默认贪婪(greedy)的匹配,仍然在第一行的结尾初停止了匹配.
3.使用re.S的时候
  import re
  ret = re.findall('^This.*line',a,re.S)
  print(ret)
  而在单行模式下,换行符被当作普通字符,被点号(.)匹配.


多行模式 re.MULTILINE
在多行模式里,文本被强制当作多行来匹配。正如上面单行模式里说的,默认情况下,一个包含换行符的字符串总是被当作多行处理。但是行首符^和行尾符$仅仅匹配整个字符串的起始和结尾。
这个时候,包含换行符的字符串又好像被当作一个单行处理。
在下面的例子里,我们希望能将三句话分别匹配出来。用re.findall( )显示所有的匹配项
a = 'This is the first line.\nThis is the second line.\nThis is the third line.'
    
1.在打印的时候直接打印出三行
print (a)
    This is the first line.
    This is the second line.
    This is the third line.
2.在不使用re.S的时候会按行匹配
    import re
    ret = re.findall('^This.*line$',a)
    print(ret)
  默认点号不匹配换行符,所以匹配的为空
3.使用re.S的时候
  import re
  ret = re.findall('^This.*line',a,re.M)
  ret = re.findall('^This.*?line',a,re.M)
  print(ret)
  匹配出了整句话,因为默认是贪婪模式
4.用问号切换成非贪婪模式:
    仍然是整句话,这是因为^和$只匹配整个字符串的起始和结束。
    在多行模式下,^除了匹配整个字符串的起始位置,还匹配换行符后面的位置;$除了匹配整个字符串的结束位置,还匹配换行符前面的位置.

总结:在单行模式下,点号(.)匹配所有的字符,所以可以说,单行模式改变了点号(.)的匹配行为,在多行模式下,多行模式改变了^和$的匹配行为

 

 分组

 

 

 

返回系列

转载于:https://www.cnblogs.com/p0st/p/10575247.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值