什么是正则表法式?

正则表法式(Regular Expression):使用正确的规则表示或者描述字符串数据。正则表达式目的是针对字符串数据的查询匹配和检索替换

正则表达式本身是一种 小型的,高度专业化 的编程语言,而在 python 中,通过内嵌集成 re模块,我们可以直接调用来实现正则匹配。正则表达式模式被编译成一系列的字节码,然后由用 c编写的匹配引擎来执行!

为什么使用正则表达式?

通过正则表达式,可以:

  • 数据验证
    测试输入字符串,以判断是否符合指定格式

  • 替换文本
    识别文档中的特定文本,完全删除该文本或者用其他文本替换它

  • 从字符串中提取子字符串
    查找文档内或输入域内特定的文本

发展历史

正则表达式的 “祖先” 可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。

1956 年, 一位叫 Stephen Kleene 的数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为 “神经网事件的表示法” 的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为 “正则集的代数” 的表达式,因此采用"正则表达式"这个术语。

随后,发现可以将这一工作应用于使用 Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson 是 Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的 qed 编辑器。

如他们所说,剩下的就是众所周知的历史了。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。

常用的字符含义

  1. 普通字符和元字符
字符 描述
. 匹配任意除换行符"\n"外的字符(在DOTALL模式中也能匹配换行符)
\ 转义字符,使后一个字符改变原来的意思
* 匹配前一个字符0次或多次
+ 匹配前一个字符1次或多次
? 匹配前一个字符0次或1次
^ 匹配字符串开头
$ 匹配字符串结尾
| 指明两项之间一个选择
{m} {m}匹配前一个字符m次
{m,n} 匹配前一个字符m至n次
{m,} 匹配前一个字符至少出现m次以上
[] 字符集。对应的位置可以是字符集中任意字符,字符集中的字符可以逐个列出,也可以给出范围,如[abc]或[a-c]。[^a-c]表示取反,即非abc
() 被括起来的表达式将作为分组

这里强调一下反斜杠 \ 的作用:

  • 反斜杠后边跟元字符去除特殊功能
  • 反斜杠后跟普通字符实现特殊功能
  • 反斜杠后跟数字,代表引用数字对应的分组所匹配的字符串
strs = "howdoyoudo"
# 此处 \2 代表引用 (do)
re.search(r"(how)(do)(you)\2",strs).group()

# howdoyoudo
  1. 预定义字符集
字符 描述
\d 数字:[0-9]
\D 非数字:[^0-9]
\s 匹配任何空白字符:[<空格>\t\r\n\f\v]
\S 匹配非空白字符:[^\s]
\w 匹配包括下划线在内的任意字母及数字:[a-zA-Z0-9_]
\W 匹配非字母字符
\A 匹配字符串开头,同^
\Z 匹配字符串结尾,同$
\b 匹配单词的边界
\B [^\b]

贪婪模式与懒惰模式

正则表达式默认是贪婪匹配,也就是匹配尽可能多的字符,加个 ? 就可以采用非贪婪模式(懒惰模式)

# 贪婪模式
re.search(r"\d+","10010").group()

# 由于是贪婪模式,直接把所有的数据全部匹配了
# 10010

# 非贪婪模式(懒惰模式)
re.search(r"\d+?","10010").group()

# 1

re模块中常用功能函数

compile()

编译正则表达式模式,返回一个对象的模式。(可以把那些常用的正则表达式编程成正则表达式对象,提高一点效率)

re.compile(pattern,flags=0)
pattern: 编译时用的表达式字符串
flags: 编译标志位,用于修改正则表达式的匹配方式

flags标志 描述
re.S 使 . 匹配保罗换行在内的所有字符
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配,法语等
re.M 多行匹配,影响^和$
re.X 这个选项忽略规则表达式中的空白和注释,并允许使用 # 来引导一个注释。这样可以让你把规则写得更美观些
re.U 根据 Unicode字符集解析字符,这个标志影响\w,\W,\b,\B

match()

从字符串起始位置开始匹配

re.match(pattern, string, flags=0)

匹配成功返回一个match object对象,否则返回 None

search()

扫描整个字符串并返回第一个成功的匹配

re.search(pattern, string, flags=0)

匹配成功返回一个match object对象,否则返回 None

match object对象 有如下方法:

  • group() 返回被 RE 匹配的字符串,可以输入组号,返回对应组号匹配的字符串
  • geoups() 返回一个包含正则表达式中所有小组字符串的元组
  • start() 返回匹配开始的位置
  • end() 返回匹配结束的位置
  • span() 返回一个元组包含匹配(开始,结束)的位置

findall()

在字符串中找到正则表达式所匹配的所有子串

re.findall(pattern, string, flags=0)

返回包含所有匹配结果的列表,如果没有找到匹配的,返回空列表

finditer()

在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回

re.finditer(pattern, string, flags=0)

split()

按照能够匹配的子串将字符串分割后返回列表

re.split(pattern, string[, maxsplit=0, flags=0])

maxsplit用于指定最大分割次数,不指定将全部分割

import re
strs = "can you speak chinese?"
print(re.split(r" ",strs))

# ['can', 'you', 'speak', 'chinese?']

sub()

使用 re 替换 string 中每一个匹配的子串后返回替换后的字符串

re.sub(pattern, repl, string, count=0, flags=0)

import re
strs = "can you speak chinese?"
print(re.sub(r" ","-",strs))

# can-you-speak-chinese?

re.sub 还允许使用 匿名函数 对匹配项的替换进行复杂的处理

import re
strs = "can you speak chinese?"
print(re.sub(r" ",lambda m:"<"+m.group(0)+">",strs))

# can< >you< >speak< >chinese?

subn()

返回替换次数

subn(pattern, repl, string, count=0, flags=0)

import re
strs = "can you speak chinese?"
print(re.subn(r" ","-",strs))

# ('can-you-speak-chinese?', 3)