正则表达式

文章目录

正则表达式

定义

正则表达式是一个特殊的字符序列,他能够帮我们检测一个字符串是否与我们所设定的字符序列相匹配。
可以快速检索文本,实现替换文本的操作。

作用

1、检查一串数字是否是电话号码
2、检测一个字符是否符合email格式
3、把一个文本里指定的单词替换为另外一个单词

一、初识正则表达式

1、findall方法:检测一串字符串是否包含指定的字符

findall(“正则表达式(pattern)”,字符串),返回想要匹配的字符串列表,如果有就返回,如果没有返回空列表
;all代表的意思找寻全部的

# a = 'C|C++|JAVA|C#|Python|Javascript'是否包含python

# 利用python内置函数index,返回首次出现的位置
a = 'C|C++|JAVA|C#|Python|Javascript|Python'
print(a.index('Python')>-1)

# 利用In
print('Python' in a)
True
True

简单的判断可以优先使用python内置函数,现在我们使用re

import re 
r = re.findall('Python', a)
print(type(r), r)
<class 'list'> ['Python', 'Python']
# 所以完整的解决
import re
a = 'C|C++|JAVA|C#|Python|Javascript|Python'
r = re.findall('Python', a)
if len(r) > 0:
    print("字符串包含Python")
else:
    print("No")
字符串包含Python

findall的返回结果几乎是没有意义的,原因是我们指定的是一个常量字符串,没有发挥正则表达式匹配的特性表达出来,正则最重要的是在于规则;如果我们把正则pattern设置为常量,是没有意义的。正则表达式存在的意义是在于规则

二、元字符与普通字符

1、 \d : 表示0-9的数字

a = ‘C0C++7JAVA8C#9Python8Javascript’,我现在问题变了,不在提取常量了,我想把字符串的数字提取出来。解决方式可以使用for循环遍历,但并不简洁

import re 
a = 'C0C++7JAVA8C#9Python8Javascript'
re.findall('\d', a)
['0', '7', '8', '9', '8']

像“Python”这样一个具体的字符叫做普用字符;“\d”称之为元字符,我们学习正则就是学习各种各样的元字符

2、 \D:表示非数字字符(空格也属于非数字)

import re 
a = 'C0C++7J8C#9Pth8Japt '
re.findall('\D', a)
['C', 'C', '+', '+', 'J', 'C', '#', 'P', 't', 'h', 'J', 'a', 'p', 't', ' ']

更多的元字符不需要强制记忆,用到啥去查就好。我们重点关注的是什么?是模式!什么是模式?比如如何匹配任意字符任意次,如何匹配任意字符至少一次等等的匹配规则

三、第一个模式:字符集

1、[ ]代表或者,中括号里面填入普通字符或元字符

现在有一个字符串,我想要找到单词的中间是c或者f的单词(关键点是“或者”)
;s = “abc, acc, adc, aec, afc, ahc”

import re
s = "abc, acc, adc, aec, afc, ahc"
re.findall('a[cf]c', s)
['acc', 'afc']
# 看一下这个效果;
import re
s = "abc, acc, adc, aec, afc, ahc"
re.findall('[cf]', s) # 其实中括号外面的两个普通字符有着定界的作用
['c', 'c', 'c', 'c', 'c', 'f', 'c', 'c']

2、^ 非:[^字符]

import re
s = "abc, acc, adc, aec, afc, ahc"
re.findall('a[^cf]c', s)  # 匹配中间不是cf的字符
['abc', 'adc', 'aec', 'ahc']

3、 a-c:表示a到c

import re
s = "abc, acc, adc, aec, afc, ahc"
re.findall('a[c-f]c', s)  # 匹配从c到f的字符
['acc', 'adc', 'aec', 'afc']

四、概括字符集

“\d”就是一个概括字符集,他可以表示0-9的数字,其实也可以用[0-9]来表示;“\D”表示非数字,也可以用[^0-9]

import re
s = '1sas9999djncfn'
print(re.findall('[0-9]', s))
print(re.findall('\d', s))
['1', '9', '9', '9', '9']
['1', '9', '9', '9', '9']

1、\w:匹配是数字+单词字符+下划线的字符串;\w = [A-Za-z0-9_]

import re
s = '1sas9999djnc  @#¥%fn_'
print(re.findall('[0-9]', s))
print(re.findall('\w', s))
print(re.findall('[A-Za-z0-9_]', s))
['1', '9', '9', '9', '9']
['1', 's', 'a', 's', '9', '9', '9', '9', 'd', 'j', 'n', 'c', 'f', 'n', '_']
['1', 's', 'a', 's', '9', '9', '9', '9', 'd', 'j', 'n', 'c', 'f', 'n', '_']

我们匹配的只是单一的字符,不是字符串

2、\W:匹配非数字+单词字符+下划线的字符串

import re
s = '1sas9999djnc  @#¥%fn_\n '
print(re.findall('\W', s))
print(re.findall('[^A-Za-z0-9_]', s))
[' ', ' ', '@', '#', '¥', '%', '\n', ' ']
[' ', ' ', '@', '#', '¥', '%', '\n', ' ']
注意的:\n:回车,空格,\t制表符属于非单词字符

3、\s:匹配空白字符,空格、回车\n,换行,制表符

import re
s = '1sas9999djnc  @#¥%fn_\n \t \r'
print(re.findall('\s', s))
[' ', ' ', '\n', ' ', '\t', ' ', '\r']

4、\S:匹配非空白字符

import re
s = '1sas9999djnc  @#¥%fn_\n \t \r'
print(re.findall('\S', s))
['1', 's', 'a', 's', '9', '9', '9', '9', 'd', 'j', 'n', 'c', '@', '#', '¥', '%', 'f', 'n', '_']

5、 . 表示除换行符\n之外其他所有的字符

五、数量词{3},{}前面的那个字符出现3次

之前我们的方式都是匹配的单个,但是如果我们线稿字符串的怎么办
import re
a = 'python 11 java8php'
re.findall('[a-z]',a)
['p', 'y', 't', 'h', 'o', 'n', 'j', 'a', 'v', 'a', 'p', 'h', 'p']
import re
a = 'python 11 java8php'
re.findall('[a-z][a-z][a-z]',a)  # 这种方式是匹配了3个,但是要匹配100个,是不可取的。我们可以用{}表示
['pyt', 'hon', 'jav', 'php']
import re
a = 'python 11 java8php'
re.findall('[a-z]{3}',a)
['pyt', 'hon', 'jav', 'php']

但这也不是我们想要的,他只能让我们匹配3个字母字符,每个单词的范围是不一样的

import re
a = 'python 11 java8php'
re.findall('[a-z]{3,6}',a)  # 匹配3,4,5,6都可以
['python', 'java', 'php']

{3,6}匹配3,4,5,6都可以,这个属于叫做贪婪操作,匹配到3的时候还会继续往后找,直到不满足他的条件为止

六、贪婪与非贪婪(?)(默认倾向于贪婪,倾向于匹配最多的)

# ?表示非贪婪的模式
import re
a = 'python 11 java8php'
re.findall('[a-z]{3,6}?',a)
['pyt', 'hon', 'jav', 'php']

七、*:匹配0次1次或者无限次; +:匹配1次或者无限次;?:匹配0次或者1次

import re
a = 'pytho0python1pythonn2 11 java8php'
re.findall('python*',a)
['pytho', 'python', 'pythonn']

为什么会出现这种情况,*作用的是他前面的那一个字符,现在*前面的是n,因此匹配n0次1次或者无限次

import re
a = 'pytho0python1pythonn2 11 java8php'
re.findall('python+',a)
['python', 'pythonn']
import re
a = 'pytho0python1pythonn2 11 java8php'
re.findall('python?',a)
['pytho', 'python', 'python']

?可以经常使用字符串去重,代替截取等操作,不要去贪婪与非贪婪的?搞混,因为非贪婪那里?前面的是一个匹配几次到几次,是一个范围,而我们的去重的这个前面并不是一个范围

八、边界匹配 ^ $

我要匹配一串数字是不是qq号,我规定QQ号是4位到8位之间,也许我们可以使用"\d{4,8}"来试一下

import re
qq = '10001'
re.findall('\d{4,8}',qq)  # 成功
['10001']
import re
qq = '1000122222'
re.findall('\d{4,8}',qq)  # 失败
['10001222']

因为他大于8的时候也可以匹配到,这个时候可以使用边界匹配,只能是在4-8的范围之内才可以匹配成功

import re
qq1 = '1000122222'
qq2 = '10000'
qq3 = '1'
print(re.findall('^\d{4,8}$',qq1)) 
print(re.findall('^\d{4,8}$',qq2)) 
print(re.findall('^\d{4,8}$',qq3))  
[]
['10000']
[]

^:表示从开头进行匹配
$: 表示从后面开始匹配

import re
s = '100001'
print(re.findall('^000',s))  # 开始是1,无法匹配
print(re.findall('000$',s))  # 后面是1,无法匹配
[]
['000']

九、组

a = ‘PythonPythonPythonPython’
判断这个字符串是否包含三个python,如果是三个字母的话可以用python{3},可以重复n,但是这个是个字符串,我们可以利用组的概念(),括起来

import re
a = 'PythonPythonPythonPython'
re.findall("(Python){3}", a)
['Python']

[]中括号是或关系,()是且关系

十、findall的第三个参数:匹配模式参数re.findall(pattern, string, flags=0)

1、re.I:忽略大小写

s = 'pythonC#daas'
a = re.findall('c#',s) # 匹配不到,因为一个大写一个小写
b = re.findall('c#',s,re.I)
a,b
([], ['C#'])

可以同时用多种模式,多种模式之间用|表示

2、re.S: 改变概括字符集.的行为使得.匹配所有的字符包括换行符(在概括字符集中.表示除换行符的所有字符)

s = 'pythonC#\ndaa\ns'
a = re.findall('c#.{1}',s,re.I|re.S)
b = re.findall('c#.{1}',s,re.I)  #.表示除换行符的所有字符
a,b
(['C#\n'], [])

十一、re.sub:正则替换(查找成功后进行替换)

re.sub(正则表达式,匹配成功后需要替换的字符串,原字符串, count(0:表示无限替换))

s = 'pythonC#javaC#C#C#'
a = re.sub("C#", "go", s, 0)
b = re.sub("C#", "go", s, 1)
a,b
('pythongojavagogogo', 'pythongojavaC#C#C#')
# python内置函数replace
s.replace('C#', "go")
'pythongojavagogogo'

re.sub强大的功能:第二个参数可以是一个函数

def convert(value):
    pass
s = 'pythonC#javaC#C#C#'
a = re.sub("C#", convert, s, 1)  # 原理:如果可以匹配到,c#将会作为convert的参数传递
a
'pythonjavaC#C#C#'
def convert(value):
    return '887'
s = 'pythonC#javaC#C#C#'
a = re.sub("C#", convert, s, 1)  # 原理:如果可以匹配到,c#将会作为convert的参数传递
a
'python887javaC#C#C#'

重要重要!!:动态的

def convert(value):
    print(value)
#     return 
s = 'pythonC#javaC#C#C#'
a = re.sub("C#", convert, s)  # 原理:如果可以匹配到,c#将会作为convert的参数传递
# a
<re.Match object; span=(6, 8), match='C#'>
<re.Match object; span=(12, 14), match='C#'>
<re.Match object; span=(14, 16), match='C#'>
<re.Match object; span=(16, 18), match='C#'>

如上,print的是一个对象,对象有一个方法叫做group可以来看一下

def convert(value):
    match = value.group()
    print(match)
s = 'pythonC#javaC#C#C#'
a = re.sub("C#", convert, s)  # 原理:如果可以匹配到,c#将会作为convert的参数传递
# a
C#
C#
C#
C#
def convert(value):
    match = value.group()
    return "!!" + match +"!!"
s = 'pythonC#javaC#C#C#'
a = re.sub("C#", convert, s)  # 原理:如果可以匹配到,c#将会作为convert的参数传递
a
'python!!C#!!java!!C#!!!!C#!!!!C#!!'

意义:我们替换的不是常量,我们想要利用不同的匹配结果实现不同的结果

我们可以举个例子:找出字符串中大于等于6的数字并将其替换成数字9,小于数字6替换成0

s = 'asn125738900276'
def convert(value):
    matched = value.group()
#     print(matched)
    if int(matched) >= 6:
        return '9'
    else:
        return '0'

re.sub('\d',convert,s)
'asn000909900099'

十二、re.match与re.search

s = 'asn125738900276'
a = re.match('\d', s)  # 从字符串的首字母开始匹配,如果没有找到结果返回空
b = re.search('\d', s)  # 搜索整个字符串
print(a)
print(b)
None
<re.Match object; span=(3, 4), match='1'>
s = '1asn125738900276'
a = re.match('\d', s)  # 从字符串的首字母开始匹配,如果没有找到结果返回空
b = re.search('\d', s)  # 搜索整个字符串
print(a)
print(b)
<re.Match object; span=(0, 1), match='1'>
<re.Match object; span=(0, 1), match='1'>
# 他们返回的都是对象,可以利用group得到匹配结果
a.group()
'1'
# 也可以利用span方法,返回源字符串的位置
a.span()
(0, 1)

另外,match和search匹配成功后立马就会停止下来,这也是和findall最大的不同

十三、重点:group分组

我想取出某两个特定字符之间的字符,比如s = ‘life is short, i use python’,我想要取出life 与 python之间的字符,我需要定界,他们之间都是一系列的单词,我们可以使用\w

s = 'life is short, i use python'
r = re.search('life\wpython', s)
print(r.group())
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-165-a4347ac3b2cc> in <module>
      1 s = 'life is short, i use python'
      2 r = re.search('life\wpython', s)
----> 3 print(r.group())


AttributeError: 'NoneType' object has no attribute 'group'

报错的原因是因为他没有匹配到,r为null,没有方法group(),因为\w只能表示一个字符,我们可以使用*

s = 'life is short, i use python'
r = re.search('life\w*python', s)
print(r.group())
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-166-c7729887cf37> in <module>
      1 s = 'life is short, i use python'
      2 r = re.search('life\w*python', s)
----> 3 print(r.group())


AttributeError: 'NoneType' object has no attribute 'group'

报错原因:\w表示的单词字符,不包括空格,我们可以用.,.可以匹配到除换行符外的所有字符

s = 'life is short, i use python'
r = re.search('life.*python', s)
print(r.group())
life is short, i use python

现在确实是匹配成功了,但是我不要life和python,我只要之间的东西。爬虫的时候我们经常要找到标签中间的内容,因此如何获得中间的内容非常重要,我们看似没有分组,但其实他只有一个,可以看成是一个分组。然后group后面是有参数的,我们可以传入0,1默认0

s = 'life is short, i use python'
r = re.search('(life.*python)', s)
print(r.group(0))
print(r.group(1))
life is short, i use python
life is short, i use python
s = 'life is short, i use python'
r = re.search('life(.*)python', s)
print(r.group(0))  # 0永远记录的完整的结果
print(r.group(1))
life is short, i use python
 is short, i use 

其实比较难以理解,但是findall同样可以实现,也更好理解

s = 'life is short, i use python'
r = re.findall('life(.*)python', s) 
print(r)
[' is short, i use ']
s = 'life is short, i use python,i love python'
r = re.search('life(.*)python(.*)python', s)
print(1, r.group(0))  # 0永远记录的完整的结果
print(2, r.group(1))
print(3, r.group(2))
print(4, r.group(0, 1, 2))
1 life is short, i use python,i love python
2  is short, i use 
3 ,i love 
4 ('life is short, i use python,i love python', ' is short, i use ', ',i love ')
# groups
s = 'life is short, i use python'
r = re.search('life(.*)python', s)
print(r.groups(0))  # 0永远记录的完整的结果
print(r.groups(1))
(' is short, i use ',)
(' is short, i use ',)

常用正则表达式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值