RegExp(一)

2022-05-07  本文已影响0人  冰菓_

正则是一个非常强大的文本处理工具,它的应用极其广泛。我们可以利用它来校验数据的有效性,比如用户输入的手机号是不是符合规则;也可以从文本中提取想要的内容,比如从网页中抽取数据;还可以用来做文本内容替换,从而得到我们想要的内容

正则表达式能做什么

在线正则表达式测试 (oschina.net)

元字符

所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,元字符是构成正则表达式的基本元件.可以把元字符大致分成这几类:表示单个特殊字符的,表示空白符的,表示某个范围的,表示次数的量词,另外还有表示断言的,我们可以把它理解成边界限定

元字符的分类与记忆技巧
特殊单字符
  1. 英文的点(.)表示换行以外的任意单个字符
  2. \d 表示任意单个数字
  3. \w 表示任意单个数字或字母或下划线
  4. \s 表示任意单个空白符
  5. 还有与之对应的三个 \D、\W 和 \S,分别表示着和原来相反的意思
空白符
  1. \r 回车符
  2. \n 换行符
  3. \f 换页符
  4. \t 制表符
  5. \v 垂直制表符
  6. \s 任意空白符
量词

我们需要匹配单个字符,或者某个部分“重复 N 次”“至少出现一次”“最多出现三次”等等这样的字符

  1. 英文的星号(*)代表出现 0 到多次
  2. 加号(+)代表 1 到多次
  3. 问号(?)代表 0 到 1 次
  4. {m,n}代表 m 到 n 次
  5. {m}代表 出现m次
  6. {m,}代表至少出现m次
示例理解\d*
示例理解\d{1,4}
范围
  1. |或,如ab|bc表示ab或bc
  2. [...]多选一,括号中任意单个元素
  3. [a-z]匹配a到z之间任意单个元素(按ASCII表,包含a,z)
  4. [^...]取反,不能是括号中的任意单个元素

为什么会有贪婪与非贪婪模式?

贪婪模式,简单说就是尽可能进行最长匹配。非贪婪模式呢,则会尽可能进行最短匹配。


>>> import re
>>> re.findall(r'a*', 'aaabb')
['aaa', '', '', '']

贪婪、非贪婪与独占模式

贪婪匹配(Greedy)

贪婪模式的特点就是尽可能进行最大长度匹配。所以要不要使用贪婪模式是根据需求场景来定的。如果我们想尽可能最短匹配呢?那就要用到非贪婪匹配模式了。

我们来看一下在字符串 aaabb 中使用正则 a* 的匹配过程
非贪婪匹配(Lazy)
>>> import re
>>> re.findall(r'a*', 'aaabb')  # 贪婪模式
['aaa', '', '', '']
>>> re.findall(r'a*?', 'aaabb') # 非贪婪模式
['', 'a', '', 'a', '', 'a', '', '', '']

这两者之间的区别 示例两者的区别
独占模式(Possessive)
贪婪模式 非贪婪模式

独占模式和贪婪模式很像,独占模式会尽可能多地去匹配,如果匹配失败就结束,不会进行回溯,这样的话就比较节省时间。具体的方法就是在量词后面加上加号(+)


独占模式

注意:需要先安装 regex 模块,pip install regex

>>> import regex
>>> regex.findall(r'xy{1,3}z', 'xyyz')  # 贪婪模式
['xyyz']
>>> regex.findall(r'xy{1,3}+z', 'xyyz') # 独占模式
['xyyz']
>>> regex.findall(r'xy{1,2}+yz', 'xyyz') # 独占模式
[]

正则回溯引发的血案

一个正则表达式引发的血案,让线上CPU100%异常! - 知乎 (zhihu.com)

New Tool: SDL Regex Fuzzer - Microsoft Security Blog

练习:
we found “the little cat” is in the hat, we like “the little cat”

其中 the little cat 需要看成一个单词

\w+|“[^”]+”

一个分支能匹配上就不会看另在一个分支,如果失败了看另外一个分支

分组与引用

不保存子组
不保留子组
括号嵌套
命名分组

由于编号得数在第几个位置,后续如果发现正则有问题,改动了括号的个数,还可能导致编号发生变化,因此一些编程语言提供了命名分组(named grouping),这样和数字相比更容易辨识,不容易出错。命名分组的格式为(?P<分组名>正则)。

分组引用

在知道了分组引用的编号 (number)后,大部分情况下,我们就可以使用 “反斜扛 + 编号”,即 \number 的方式来进行引用

分组引用在查找中使用
前面出现的单词再次出现

(\w+)代表分组,此时只有一个分组,“\1”代表第一个分组的内容于是,该正则意思是:某单词+空格+某单词,这样就实现了连续重复单词的匹配

分组引用在替换中使用
分组引用在替换中使用
import re
text = '''the little cat cat is in the hat hat hat , we like it'''
pattern = re.compile(r'''(\w+)(\s+\1)+''')
a = pattern.findall(text)
print(re.sub(pattern, r"\1", text))

# (\s+\1)+ 里面的(\s+\1)重复一次或多次

不区分大小写模式(Case-Insensitive)

不区分大小写是匹配模式的一种。当我们把模式修饰符放在整个正则前面时,就表示整个正则表达式都是不区分大小写的。模式修饰符是通过 (? 模式标识) 的方式来表示的。 我们只需要把模式修饰符放在对应的正则前,就可以使用指定的模式了。


案例一
案例二
案例三

点号通配模式(Dot All)

英文的点(.)有什么用吗?它可以匹配上任何符号,但不能匹配换行。


案例一

多行匹配模式(Multiline)

通常情况下,^ 匹配整个字符串的开头,匹配整个字符串的结尾。多行匹配模式改变的就是 ^ 和 的匹配行为

案例一

多行模式的作用在于,使 ^ 和 $ 能匹配上每行的开头或结尾,我们可以使用模式修饰符号 (?m) 来指定这个模式。


案例二

注释模式(Comment)


(\w+)(?#word) \1(?#word repeat again)

HTML 标签是不区分大小写的,比如我们要提取网页中的 head 标签中的内容,用正则如何实现呢?
正则前面的 (?i) (?s) (?m) (?is) (?im)

单行模式

[-+](?si)<head>(.*)<\/head>
上一篇下一篇

猜你喜欢

热点阅读