在 Python 程序中,模式字符串使用如下特殊的语法来表示一个正则表达式:
- 字母和数字表示它们自身,一个正则表达式模式中的字母和数字匹配同样的字符串;
- 当大多数字母和数字前加一个反斜杠时,它们会拥有不同的含义;
- 标点符号只有被转义时才匹配自身,否则它们表示特殊的含义;
- 反斜杠本身需要使用反斜杠转义;
- 因为正则表达式通常都包含反斜杠,所以最好使用原始字符串来表示它们。模式元素(如r'\t',等价于'\\t')匹配相应的特殊字符。
表 1 列出了正则表达式模式语法中的特殊元素。如果使用模式的同时提供了可选的标志参数,则某些模式元素的含义会发生改变。
| 模式 | 描述 |
|---|---|
| ^ | 匹配字符串的开头 |
| $ | 匹配字符串的末尾 |
| . | 匹配任意字符,除换行符之外,当指定 re.DOTALL 标记时,则可以匹配包括换行符的任意字符 |
| [...] | 用来表示一组字符,单独列出,如 [amk] 匹配 'a' 'm' 或 'k' |
| [^...] | 不在 [] 中的字符,如[^abc] 匹配除 a、b、c 之外的字符 |
| re* | 匹配 0 个或多个表达式 |
| re+ | 匹配 1 个或多个表达式 |
| re? | 匹配 0 个或 1 个由前面的正则表达式定义的片段,非贪婪方式 |
| re{ n} | 精确匹配前面的 n 个表达式 |
| re{ n, m} | 匹配 n~m 次由前面的正则表达式定义的片段,贪婪方式 |
| a | b | 匹配 a 或 b |
| (re) | 匹配括号内的表达式,也表示一个组 |
| (?imx) | 正则表达式包含 3 种可选标志—— I、m 或 x。只影响括号中的区域 |
| (?-imx) | 正则表达式关闭 I、m 或 x 可选标志。只影响括号中的区域 |
| (?: re) | 类似于(…),但是不表示一组 |
| (?imx: re) | 在括号中使用I、m 或 x 可选标志 |
| (?-imx: re) | 在括号中不使用 I、m 或 x 可选标志 |
| (?#...) | 注释 |
| (?= re) | 前向肯定界定符。如果所含正则表达式以…表示,则在当前位置匹配时成功;否则,失败。一旦所含表达式已经使用,那么模式的剩余部分还要尝试匹配界定符的右边 |
| (?! re) | 前向否定界定符。与肯定界定符相反,当所含表达式不在字符串当前位置匹配时成功 |
| (?>re) | 匹配的独立模式,省去回溯 |
| \w | 匹配字母数字 |
| \W | 匹配非字母数字 |
| \s | 匹配任意空白字符,等价于[\t\n\r\f\v] |
| \S | 匹配任意非空白字符 |
| \d | 匹配任意数字,等价于 [0~9] |
| \D | 匹配任意非数字 |
| \A | 匹配字符串开始 |
| \Z | 匹配字符串结束,如果存在换行,则只匹配到换行前的结束字符串 |
| \z | 匹配字符串结束 |
| \G | 设置匹配必须出现在上一个匹配结束的地方 |
| \b | 匹配一个单词边界,也就是指单词和空格间的位置。例如,'er\b'可以匹配 "never" 中的 'er',但不能匹配 "verb" 中的 'er' |
| \B | 匹配非单词边界,'er\B' 能匹配"verb"中的' er' ,但不能匹配 "never" 中的 'er' |
| \n、\t | 分别用于匹配一个换行符和一个制表符 |
| \1...\9 | 匹配第 n 个分组的子表达式 |
| \10 | 如果正在被匹配,则匹配第 n 个分组的子表达式;否则,指八进制字符码的表达式 |
表 2 列出了在 Python 程序中使用常用正则表达式的实例。
| 实例 | 描述 |
|---|---|
| python | 匹配 “python” |
| [Pp]ython | 匹配“Python”或“python” |
| rub[ye] | 匹配“ruby”或“rube” |
| [aeiou] | 匹配中括号内的任意一个字母 |
| [0-9] | 匹配任何数字,类似于 [0123456789] |
| [a-z] | 匹配任何小写字母 |
| [A-Z] | 匹配任何大写字母 |
| [a-zA-Z0-9] | 匹配任何字母及数字 |
| [^aeiou] | 匹配除 a、e、i、o 和 u 字母以外的所有字符 |
| [^0-9] | 匹配除数字之外的字符 |
| . | 匹配除“\n”的任何单个字符。要匹配包括在内的任何字符,使用像的模式 |
| \d | 匹配一个数字字符,等价于[0~9] |
| \D | 匹配一个非数字字符,等价于[^0~9] |
| \s | 匹配任何非空白字符,等价于[^\f\n\r\t\v] |
| \w | 匹配包括下画线的任何单词字符,等价于'[A-Za-z0-9_]' |
| \W | 匹配任何非单词字符,等价于'[^A-Za-z0-9_]' |
下面的实例文件 cao.py 演示了使用正则表达式统计指定文件中函数和变量的过程。Python 语法规定,函数定义必须以“def”开头,为了降低实例的难度,在此假设 Python 函数的编写规范是在关键字“def”后跟一个空格,然后是函数名和参数。没有考虑使用多个空格的情况。
在Python程序中,因为变量一般不需要事先声明,可以直接赋值,所以要想统计文件中的变量信息,需要先处理变量直接赋值的情况,然后通过匹配单词后接“=”的情况查找变量名。为了简化起见,仅考虑比较规范整洁的写法,即规定变量名与“=”之间有一个空格。
另外,添加了一种在 for 循环语句中直接使用的变量类型,在实例中特别处理了 for 循环的情况。在这里,实例并没有处理变量名重复的情况。
import re #导入模块re
import sys #导入模块 sys
def TongjiFunc(s): #定义函数TongjiFunc()统计函数r = re.compile( r''' #调用函数compile()(?<=def\s) #设置前面必须有函数标志 def,并且紧跟一个空格\w+ #匹配函数名\(.*?\) #匹配参数 、(?=:) #后面紧跟一个冒号“:”''',re.X | re.U) #设置编译选项,忽略模式中的注释return r.findall(s)def TongjiVar(s): #定义函数TongjiVar()统计变量vars = [] #定义存储变量的列表r = re.compile(r'''\b #匹配单词\w+ #匹配变量名(?=\s=) #处理特殊情况—— 变量赋值''',re.X | re.U) #设置编译选项,忽略模式中的注释vars.extend(r.findall(s))r = re.compile( r''' (?<=for\s) #处理for语句中的变量\w+ #匹配变量名\s #匹配空格(?=in) #匹配in关键字''',re.X | re.U) #设置编译选项,忽略模式中的注释vars.extend(r.findall(s))return vars
if len(sys.argv) == 1: #判断是否输入了命令,如果没有输入,必须输入要处理的文件sour = input('亲,请输入文件路径') #提示输入要处理文件的路径
else: #如果输入了命令sour = sys.argv[1] #获取命令行参数
file = open(sour,encoding="utf-8") #打开文件
s = file.readlines() #以行读取文件的内容
file.close() #关闭文件
print('********************************')
print('文件',sour,'中存在的函数有:')
print('********************************')
i = 0 #i表示函数所在的行号
for line in s: #循环遍历文件的内容,匹配里面的函数,输出函数所在的行号,输出函数的原型i = i + 1function = TongjiFunc(line) #调用统计函数的函数if len(function) == 1: #如果行数是1print('Line: ',i,'\t',function[0])
print('********************************')
print('文件',sour,'存在的变量有:')
print('********************************')
i = 0 #i表示变量所在的行号
for line in s: #循环遍历文件的各行,匹配里面的变量,输出变量所在的行号,输出变量名i = i + 1var = TongjiVar(line) #调用统计变量的函数if len(var) == 1:print('Line: ',i,'\t',var[0])
在上述实例代码中,首先定义了一个能够获取文件中函数名的函数 TongjiFunc(s),然后定义了一个能够获取变量名的函数 TongjiVar(s),最后分别调用它们执行查找和获取操作。执行文件 cao.py 后的效果如图 1 所示。

图 1:执行文件 cao.py 后的效果
下面的实例 gaoji1.py 演示了如何创建一个生成数据集的脚本。
from random import randrange, choice
from string import ascii_lowercase as lc
from sys import maxsize
from time import ctime①tlds = ( 'com', 'edu', 'net', 'org', 'gov' )for i in range(randrange(5, 11)):dtint = randrange(maxsize) #选择日期dtstr = ctime(dtint) #日期字符串② llen = randrange(4, 7) #登录名比较短login = ''.join(choice(lc) for j in range(llen))dlen = randrange(llen, 13) #域比较长dom = ''.join(choice(lc) for j in range(dlen))print('%s::%s@%s.%s::%d-%d-%d' % (dtstr, login,dom, choice(tlds), dtint, llen, dlen))
上述代码是一个生成数据集的脚本,能够简单地将生成的字符串集显示到标准输出中,该输出很容易重定向到测试文件。上述脚本能够生成拥有 3 个字段的字符串,由一对冒号:或者一对双冒号::分隔。其中第一个字段是随机(32 位)整数,该整数将被转换为一个日期;第 2 个字段是一个随机生成的电子邮件地址;第3个字段是一个由单横线-分隔的整数集。
其中 tlds 是由一组高级域名格式组合而成的,当需要随机生成电子邮件地址时,可以从中随机选出一个。每当执行文件 gendata.py,就会生成 for 语句之前的输出(该脚本对于所有需要随机整数的场景都使用函数 random.randrange())。对于每一行,选取范围(0~231−1) 中所有可能的随机整数,然后使用函数 time.ctime() 将该整数转换为日期。
在 Python 程序中,系统时间和大多数基于 POSIX 的计算机一样,两者都使用从“epoch”到今天的秒数,其中 epoch 是指 1970 年 1 月 1 日格林尼治时间的午夜。如果选择一个 32 位整数,那么该整数将表示从epoch到最大可能时间(即epoch后的232s)之间的某个时刻。
①~② 的功能是构造登录名长度是 4~7 个字符的邮件地址,这是通过使用 randrange(4,8) 实现的。为了将它们放在一起,需要随机选择 4~7 个小写字母,将所有字母逐个连接成一个字符串。函数 random.choice() 的功能就是接受一个序列,然后返回该序列中的一个随机元素。在上述代码中,string.ascii_lowercase 是字母表里拥有 26 个小写字母的序列集合。
注意:不能将限定符与定位点一起使用。
在 Python 程序中,因为在换行符或者字边界的前面或后面不能有一个以上空的位置,所以不允许诸如^*之类的表达式。如果要匹配一行文本开始处的字符,请在正则表达式的开始使用^字符。不要将^的这种用法与中括号表达式内的用法混淆。如果要匹配一行文本结束处的字符,请在正则表达式的结束处使用$字符。

















