正则表达式与re模块

正则表达式与re模块

一、引入

字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在。比如判断一个字符串是否是合法的Email地址,虽然可以编程提取@前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦,而且代码难以复用。

正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。

所以我们判断一个字符串是否是合法的Email的方法是:

  1. 创建一个匹配Email的正则表达式;
  2. 用该正则表达式去匹配用户的输入来判断是否合法。

注意:正则表达式与语言无关,是一套规则,用来匹配字符串

1.re模块介绍

  • re模块就是Python内置的模块

2.什么是正则

  • 正则表达式就是对字符串操作的一种逻辑(规则),就是事先定义好一些特定的字符组合,组合一个**“规则字符串”**,然后使用这个“规则字符串”来对字符串进行过滤
  • 在Python中通过内置的re模块来使用正则表达式,它提供了所有正则表达式的功能。

3.正则表达式的大致匹配过程

  • 定义一个**“规则字符串”**,然后依次与文本中的字符进行对比
  • 如果每一个字符都能匹配,那么就匹配成功,否则匹配失败
  • 如果表达式中有量词或者边界,这个过程会稍微有一些不同

4.正则规则

  • 所有规则中的字符刚好匹配到字符串中的内容
  • 在正则表达式中能够帮助我我们表示匹配内容的符号都是正则中的元字符。
5.作用
  1. 检测一个输入的字符串是否合法。
    • web开发项目,表单验证。用户输入一个内容的时候,我们要提前做检测,将不合法的内容直接返回,能够提高程序的效率并且减轻服务器的压力。
  2. 从一个大文件中找到所有符合规则的内容。
    • 日志分析、爬虫。能够高效的从一大段文字中快速找到符合规则的内容。
5.常用正则表达式表
模式描述
\w匹配字母数字及下划线
\W匹配非字母数字下划线
\s匹配任意空白字符,等价于[\t,\n,\r,\f]
\S匹配任意非空字符
\d匹配任意数字,等价于[0-9]
\D匹配任意非数字
\A匹配以xx字符串开始
\Z匹配xx字符串结束,如果是存在换行,只匹配到换行前的结束字符串
\z匹配xx字符串结束
\G匹配最后匹配完成的位置
\n匹配一个换行符
\t匹配一个制表符
^匹配字符串的开头
$匹配字符串的末尾
.匹配任意字符, 除了换行符, 当 re.DOTALL 标记被指定时, 则可以匹配包括换行符的任意字符
[…]用来表示一组字符, 单独列出:[amk] 匹配 ‘a’,‘m’ 或 ‘k’
[^…]不在 [ ] 中的字符:[^abc] 匹配除了a, b, c之外的字符
*匹配0个或多个的表达式
+匹配1个或多个的表达式
?匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
{n}精确匹配n个前面表达式。
{n,m}匹配n到m次由前面的正则表达式定义的片段,贪婪方式
alb匹配a或b
()匹配括号内的表达式,也表示一个组

ps : [...] 讲解 : [ ]所有的特殊字符在字符集中都失去其原有的特殊含义,在字符集中如果要使用]-^,可以在前面加上反斜杠,或把]-放在第一个字符,把^放在非第一个字符

更多正则表达式 : https://www.jb51.net/tools/regexsc.htm

二、普通字符和元字符

正则表达式就是由一些普通字符和元字符组成的

  • 普通字符就字母和数字
  • 元字符就是具有特殊意义的字符,常用于表单验证

三、正则边界(定位字符)

在正则表达式中,如果直接给出字符,就是精确匹配。

  • ^ : 会匹配字符串或者行的起始位置, 在 [ ] 中表示 ‘‘非’’(!)
  • $ : 会匹配字符串或行的结束位置 (在 \n 之前)
  • ^,$ 一起则表示精确匹配包裹在两个符号之间的字符串
  • \A : 指定匹配必须出现在字符串的开头(忽略 Multiline 选项)
  • \Z : 指定匹配必须出现在字符串的结尾或字符串结尾的 \n 之前(忽略 Multiline 选项)

四、量词

用来匹配前面的字符出现的次数

  • : 重复零次或更多 (>=0)
  • : 重复一次或更多次 ( >=1)
  • : 重复零次或一次 (0 || 1),默认贪婪,也可用作防止贪婪
  • {} : 重复多少次的意思 可以有多少个
  • {n}:表示明确匹配n次。
  • {n,}:表示至少匹配n次,至多m次。
  • {n,m}表示至少匹配n次,至多m次。

五、转义符\

  • 原本有特殊意义的字符,到了表达它本身的意义的时候,需要转义。
  • 有一些有特殊意义的内容,放在字符组[ ]中,也会取消它的特殊意义。
[.]
[().*+?]

# 横杠-在字符组中表示范围,如果不希望它表示范围,则需要转义,或者放在字符组的最前面或最后面

# 匹配15位身份证号,1-9开头
[1-9]\d{14}

# 匹配18位身份证号,1-9开头,最后一位为数字或x
[1-9]\d{16}[\dx]

# 匹配15位或18位身份证号,方法一:
^([1-9]\d{16}[\dx]|[1-9]\d{14})$   # 必须将匹配更长的表达式放前面,否则将只能匹配15位身份证号

# 方法二: 两种身份证号格式的前15位是相同规律,后面三位要么都出现,要么都不出现
^[1-9]\d{14}(\d{2}[\dx])?$

六、re模块的常用的功能函数

函数说明
re.match( )从字符串的起始位置匹配, 匹配成功, 返回一个匹配的对象, 否则返回None
re.search( )扫描整个字符串并返回第一个成功的匹配
re.findall( )在字符串中找到正则表达式所匹配的所有子串, 并返回一个列表, 如果没有找到匹配的, 则返回空列表
re.split( )将一个字符串按照正则表达式匹配结果进行分割, 返回列表类型
re.finditer( )在字符串中找到正则表达式所匹配的所有子串, 并把它们作为一个迭代器返回
re.sub( )把字符串中所有匹配正则表达式的地方替换成新的字符串
re.complie( )将正则表达式传入, 返回一个匹配对象, 一般与其他方法组合使用

七、正则使用实例

一对一匹配

print("hello".replace("llo",'ooo')) #heooo
print("hello".find("abccd")) # -1 (找不到返回-1)
print('hello'.find("he")) #0

正则匹配

  • \w\W
import re

print(re.findall("\w","ab* 12$ _"))  # ['a', 'b', '1', '2', '_']
print(re.findall("\W","ab* 12$ _"))  # ['*', ' ', '$', ' ']
  • \s\S
print(re.findall("\s","ab* 12$ _ "))  # [' ', ' ', ' ']
print(re.findall("\S","ab* 12$ _ "))  # ['a', 'b', '*', '1', '2', '$', '_']
  • \d\D
print(re.findall("\d","ab* 12$ _"))  # ['1', '2']
print(re.findall("\D","ab* 12$ _"))  # ['a', 'b', '*', ' ', '$', ' ', '_']
  • \A\Z
print(re.findall("\Aab","ab* 12$ _"))  # ['ab']
print(re.findall("\Aaa","ab* 12$ _"))  # [] 没匹配到为空
print(re.findall("_\Z","ab* 12$ _"))   # ['_']
print(re.findall("0\Z","ab* 12$ _"))   # [] 没匹配到为空
  • \t\n
print(re.findall("\t","ab* 12$ \t_"))    # ['\t']
print(re.findall("\n","ab* 12$ \n_"))    # ['\n']

🔰"\s" 可以匹配"\t""\n"
print(re.findall("\s","ab* \t12$ _\n"))  # [' ', '\t', ' ', '\n']
  • ^\$
print(re.findall("^ab","ab* 12$ _"))   # ['ab']
print(re.findall("_$","ab* 12$ _\n"))  # ['_']

重复匹配

  • : 匹配一个任意字符
print(re.findall("a.b","a&b"))  # ['a&b']
print(re.findall("a.b","a2b"))  # ['a2b']
print(re.findall("a.b","acb"))  # ['acb']
print(re.findall("a.b","a b"))  # ['a b']
print(re.findall("a.b","a\tb")) # ['\t']
print(re.findall("a.b","a\nb")) # [] (换行符匹配不到,匹配为空)
print(re.findall("a.b","a b a*b abb a_b"))  # ['a b', 'a*b', 'abb', 'a_b']
print(re.findall("a.b","a\nb",re.S))        # ['a\nb'] (加入参数, 包含换行)
print(re.findall("a.b","a\nb",re.DOTALL))   # ['a\nb'] (同上效果一样)
  • : 匹配零个或多个
print(re.findall("a*","aaaa aa"))        # ['aaaa', '', 'aa', ''] (零个或多个a)
print(re.findall("ab*","abab aa"))       # ['ab', 'ab', 'a', 'a'] (一个a零个或多个b)
print(re.findall("a*b","ababaaaba aa"))  # ['ab', 'ab', 'aaab'] (零个或多个a一个b)
print(re.findall("ab*","bbbbbbbb"))      # [] (没有匹配到一个a零个或多个b)
  • : 匹配零个或一个
print(re.findall("ab?","a"))     # ['a']
print(re.findall("ab?","abbb"))  # ['ab']
  • : 匹配一个或多个
print(re.findall("a+","bbb"))    # []
print(re.findall("a+","ab"))     # ['a']
print(re.findall("ab+","ab"))    # ['ab']
print(re.findall("ab+","abbb"))  # ['abbb']
  • {n,m} : 匹配 n~m 之间个
print(re.findall("a{9}","aaaa"))     # [] (匹配9个b,没有)
print(re.findall("a{3}","aaaa"))     # ['aaa']
print(re.findall("a{2}","aaaa"))     # ['aa', 'aa']
print(re.findall("ab{2}","aabbb"))   # ['abb']
print(re.findall("ab{2,6}","abbb"))  # ['abbb'] (一个a,2~6个b)
print(re.findall("ab{1,}","abbb"))   # ['abbb'] (相当于 ab+)
print(re.findall("ab{0,}","abbb"))   # ['abbb'] (相当于 ab*)
  • [ ] : 放一组字符,逐个匹配
print(re.findall("a[*1_c-]b","a*ba1b a_baaba-b"))     # ['a*b', 'a1b', 'a_b', 'a-b']
print(re.findall("a[^*1c-]b","a*ba1b a_baab"))        # ['a_b', 'aab'] (匹配非 [] 内的字符)
print(re.findall("a[0-9]b","a*ba1b a3baa2b"))         # ['a1b', 'a3b', 'a2b'] (0~9)
print(re.findall("a[a-z]b","a*banb apbaaob"))         # ['anb', 'apb', 'aob'] (a~z)
print(re.findall("a[a-zA-Z0-9]b","a*banb aPbaa7b"))   # ['anb', 'aPb', 'a7b']
print(re.findall("a[^a-zA-Z0-9]b","a*banb aPbaa7b"))  # ['a*b'] (非a~z,A~Z,0~9)

1.一个字符组中括号只表示一个字符位置
[0-9]:匹配0123456789中任意一个数字
[a-z]:匹配任意一个小写字母
[A-Z]:匹配任意一个大写字母
[a-zA-Z]:匹配任意一个小写字母
[0-9a-z]:匹配任意一个数字或小写字母
    
注:不能使用[a-Z],横杠是以ASCII的顺序进行对比,A的ASCII码是65,a的ASCII码是97,顺序只能从小到大.
也不推荐使用[A-z],ASCII码65122之间除了包含所有大小写字母,还包含6个符号.

2.要匹配两个字符可以使用两个字符组
[0-9][a-zA-Z]:匹配一个数字和一个字母
  • [^]表示取反;如果要在中括号内匹配^-特殊字符,若没有使用转义符的话,应该将符号放在中括号开头或结尾。
  • ( ) : 分组
print(re.findall('ab+','ababab123')) 
# ['ab', 'ab', 'ab']
print(re.findall('(ab)+123','ababab123')) 
# ['ab'],匹配到末尾的 ab123 中的 ab
print(re.findall('(?:ab)+123','ababab123')) 
# findall的结果不是匹配的全部内容,而是组内的内容, ?: 可以让结果为匹配的全部内容
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))
# ['http://www.baidu.com']
print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com">点击</a><a href="http://www.aiqiyi.com">点击2</a>'))
# ['href="http://www.baidu.com"', 'href="http://www.aiqiyi.com"']
  • a|b : 左右两边都匹配
print(re.findall("shawn|song","shawn is man song is shawn"))  # ['shawn', 'song', 'shawn'] (不加括号全局匹配)
print(re.findall("A(?:abc|cba)A","AabcA"))  # ['AabcA']
print(re.findall("com(?:puter|e)","come on! Here is a computer "))  # ['come', 'computer']
  • \ : 转义字符
# 转义字符,使后一个字符改变原来的意思。
# 如果字符串中有字符 * 需要匹配,可以使用 \* 或者字符集 [*]
print(re.findall("a.b","a1b a.b"))   # ['a1b', 'a.b']
print(re.findall("a\.b","a1b a.b"))  # ['a.b']

匹配所有数字类型实例

print(re.findall("\d+\.?\d*","12as3.45qw2k7"))  # ['12', '3.45', '2', '7']

.* 贪婪匹配示例

  • 匹配更多的字符
print(re.findall("a.*b","a11b22222b33"))  # ['a11b22222b']

.*? 非贪婪匹配示例

  • 尽可能少的匹配
print(re.findall("a.*?b","a11b22222b3"))  # ['a11b']

八、re 模块常用方法示例

1、re.findall( pattern,string)

  • 返回所有满足匹配的结果, 按照顺序依次放入列表中, 如果没有匹配结果, 返回一个空列表
print(re.findall("abc","112abc333"))  # ['abc']
print(re.findall("abcd","12abc333"))  # []

2、re.search( pattern,string)

  • 匹配整个字符串, 只到找到第一个匹配然后返回一个包含匹配信息的对象(re.Match对象)
  • 该对象可以通过调用 group( ) 方法得到匹配的字符串,如果字符串没有匹配,则返回None
  • 如果没有匹配到值就调用 group( ) 方法, 抛出异常
print(re.search("abc","112abc333abc"))  
# <re.Match object; span=(3, 6), match='abc'>
print(re.search("abc","112abc333abc").group())  # abc

print(re.search("abcd","12abc333abc"))          # None
print(re.search("abcd","12abc333abc").group()) 
# 报错 "AttributeError" (因为没拿到这个对象,所以没有group()属性

3、re.match( pattern,string)

  • re.search 功能相同, 但必须匹配起始位置, 不然返回 None
  • 可以使用上尖号 ^ : re.search("^abc",“abc”) 实现 re.match() 功能
print(re.match("abc","abc22abc"))  # <re.Match object; span=(0, 3), match='abc'>
print(re.match("abc","abc22abc").group())  # abc

print(re.match("abc","1abc22abc"))  # None
print(re.search("^abc","1abcabc"))  # None  (re.search)

4、re.split( pattern,string)

  • 规定字符作为分隔符对字符串进行切分, 切分结果放入列表, 没匹配到返回原字符串列表
  • 将规定字符放入 [ ] 则是逐个匹配
print(re.split("o","abcoabcoabc"))   # ['abc', 'abc', 'abc']
print(re.split("ab","abcoabcoabc"))  # ['', 'co', 'co', 'c']

print(re.split("d","abcoabcoabc"))  # ['abcoabcoabc'] (没匹配到返回原字符串)

print(re.split("[o]","abcoabcoabc"))  # ['abc', 'abc', 'abc']
print(re.split("[ob]","abcoabcoabc"))   # ['a', 'c', 'a', 'c', 'a', 'c']
print(re.split("[oba]","abcoabcoabc"))  # ['', '', 'c', '', '', 'c', '', '', 'c']
# 先使用"o"匹配进行分隔, 得到结果"['abc', 'abc', 'abc']", 在使用"b"进行匹配分隔,得到结果后再使用"a"匹配进行分隔

5、re.sub( ) 和 re.subn( )

  • 匹配**“规则字符**”, 并将其改成指定字符, 返回改变后的字符串, 后面可跟数量参数, 不指定默认替换所有
  • re.subn( ) 返回一个元组, 第二个元素返回的是替换的个数
print(re.sub("a","A","i am a man"))   # i Am A mAn
print(re.sub("a","b","i am a man"))   # i bm b mbn
print(re.sub("am","b","i am a man"))  # i b a man
print(re.sub("a","AA","i am a man"))  # i AAm AA mAAn

print(re.sub("a","A","i am a man",1))  # i Am a man
print(re.sub("a","A","i am a man",2))  # i Am A man
print(re.sub("a","A","i am a man",3))  # i Am A mAn
print(re.sub("a","A","i am a man",9))  # i Am A mAn (超出字符串中最大的个数不会报错,而是替换所有)

print(re.subn("a","A","i am a man"))    # ('i Am A mAn', 3) (显示替换的个数)
print(re.subn("a","A","i am a man",2))  # ('i Am A man', 2)

6、re.compile( )

  • 在该函数中传入**“规则字符**”, 返回一个对象, 它单独使用没有任何意义, 需要和 findall( ), search( ), match( ) 组合使用
obj = re.compile("\d{2}")
print(obj)  # re.compile('\\d{2}')

print(obj.findall("ab12c33b44"))         # ['12', '33', '44']
print(obj.search("ab12c33b44").group())  # 12
print(obj.match("12c33b44").group())     # 12 (注意,必须在开头就匹配到,不然group会报错)

7.总结:

当我们在Python中使用正则表达式时,re模块内部会干两件事情:

  1. 编译正则表达式,如果正则表达式的字符串本身不合法,会报错;
  2. 用编译后的正则表达式去匹配字符串。

九.非捕获模式 : ?: ?! ?=

字符描述
(?:pattern)匹配pattern, 但不捕获匹配结果。
(?=pattern)零宽度正向预查,不捕获匹配结果。
(?!pattern)零宽度负向预查,不捕获匹配结果。
(?<=pattern)零宽度正向回查,不捕获匹配结果。
(?<!pattern)零宽度负向回查,不捕获匹配结果。

1、?! 示例

  • ?! pattern,表示在没有配到pattern的字符串的前提下,再进行后续的正则表达式匹配,后续匹配仍然从被匹配字符串的头开始
🍓 匹配密码,密码必须是由6位数字与字母组成,并且不能是纯数字也不能是纯字母
while 1:
    passwd = input(">>").strip()
    res = re.match("(?![\d]+$)(?![a-zA-Z]+$)[\da-zA-Z]{6}$",passwd)
    if res:
        print(f"密码:{res.group()}")
    else:
        print("密码不符合规则")

2、?= 示例

  • ?= pattern,表示在配到pattern的字符串的前提下,再进行后续的正则表达式匹配,后续匹配仍然从被匹配字符串的头开始
🍓 匹配密码,必须包含大写,小写和数字,和特殊字符(!,@,#,%,&),且大于6位
while 1:
    passwd = input("请输入密码>>").strip()
    res = re.search("(?=.*[\d])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#%&])^([\da-zA-Z!@#%&]{7,})$",passwd)
    if res:
        print(res.group())
    else:

参考资料

官网:https://docs.python.org/zh-cn/3/library/re.html?highlight=re#module-re

廖雪峰的网站:https://www.liaoxuefeng.com/wiki/1016959663602400/1017639890281664

博客园:https://www.cnblogs.com/ChiRou/p/14278065.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贾维斯Echo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值