正则表达式概述

正则表达式在文本验证、查找和替换方面有着广泛的应用。本节介绍正则表达式的基本概念,结合简单的实例让您对正则表达式的编写和应用有感性认识。[大谦Excel,dqexcel点com]

什么是正则表达式?

关于文本的查找和替换,常见的有两种典型应用。一种是在Windows资源管理器中查找指定目录下的文件,通常是指定文件名称或文件名称中的一部分进行查找,还可以指定通配符?和*,分别表示一个字符或任意个字符,比如“*.exe”表示所有可执行文件。另一种是在记事本、Word软件等办公软件中一般都有的查找功能和替换功能。这两种情况下给出的搜索文本都是简单的正则表达式。

很多时候,我们需要匹配形式更复杂的文本,比如从一个网页的文本中提取出电话号码、手机号码、电子邮箱等,比如从给定文本中提取出以某字符串打头、以某字符串结尾的子文本等,这就需要用到正则表达式。

正则表达式是由普通字符和一些元字符组成的逻辑表达式,普通字符包括数字和大小写字母,元字符则用字符或字符的组合表达特殊的含义。所以,正则表达式其实就是按照事先定义好的规则来组合普通字符和元字符,表达字符串的匹配逻辑,执行查找时对表达式进行解析,了解它所表达的意图并进行匹配,最后找到需要查找的内容。

使用正则表达式

由于文本查找和替换的需求很常见,在各种语言中都有正则表达式的内容。在不同的语言中,正则表达式的编写规则几乎是相同的,区别在于编译和处理正则表达式的语法有所不同,即使用正则表达式的方式有所不同。

【Excel VBA】

Excel VBA中使用正则表达式,首先要导入正则对象。进入Excel的VBA编程环境,在“工具”菜单中单击“引用…”选项,打开“引用”对话框如图8-1所示。在“可使用的引用”列表框中找到并选择“Microsoft VBScript Regular Expressions 5.5”选项,单击“确定”按钮。

Document Image

图8-1 引用正则对象相关的库

引用正则对象相关的库以后,就可以用Excel VBA的对象浏览器进行查看了。在“视图”菜单中单击“对象浏览器…”选项,打开对象浏览器如图8-2所示。在左上角的下拉式列表框中单击“VBScript_RegExp_55”选项,对象浏览器中的“类”列表框中显示库中的所有类。选择一个类,在右侧列表框中显示该类的所有成员。选择一个成员,在下面的文本框中显示成员说明。

Document Image

图8-2 对象浏览器

对象浏览器中,RegExp类生成的对象即正则对象,Excel VBA中使用正则对象实现正则表达式的功能,即文本内容的查找和替换。MatchCollection类生成的对象为进行文本查找后得到的匹配文本的集合,而Match类生成的对象表示其中的某个匹配文本,SubMatches类生成的对象在正则表达式有分组的情况下保存各分组的数据。具体什么是分组,请参考下一节中捕获分组和非捕获分组的内容。

首先生成正则对象,可以有后期绑定和前期绑定两种方法。

使用后期绑定时,将变量声明为Object类型,用CreateObject函数创建正则对象。

code.vba
Dim objReg As Object
Set objReg=CreateObject("VBScript.RegExp")

前期绑定有两种使用方式,即

code.vba
Dim objReg As RegExp
Set objReg=New RegExp

code.vba
Dim objReg As New RegExp

它们都将变量直接声明为RegExp类型。

前期绑定与后期绑定比较,代码运行效率上有明显优势,所以建议使用前期绑定创建正则对象。创建正则对象以后,用它结合正则表达式实现文本内容的查找和替换。

一、查找

使用RegExp对象,用它的Pattern属性指定正则表达式,用Execute方法执行查找。正则表达式的编写规则将在下一节进行详细介绍,作为示例,本节使用一些比较简单的正则表达式进行演示。

用Execute方法查找的结果保存在MatchCollection集合中,使用For Each可以遍历集合中的每个Match对象,即匹配结果对象。默认时,找到第1个匹配对象即停止查找,设置正则对象的Global属性的值为True,可以找出全部匹配结果;设置IgnoreCase属性的值为True,不区分大小写。

下面的代码在给定的字符串"A1B2C3"中找出所有非数字的字符。

code.vba
Sub Test()
  Dim objReg As New RegExp  '生成正则对象
  Dim strT As String
  Dim mcT As MatchCollection  '保存查找结果
  Dim matT As Match  '每个查找结果
  strT = "A1B2C3"  '给定字符串
  With objReg
    .Global = True  '全局查找
    .Pattern = "\D"  '指定正则表达式,定义匹配规则,非数字
    Set mcT = .Execute(strT)  '执行查找
  End With
  For Each matT In mcT  '遍历每个查找结果
    If matT <> " " Then Debug.Print matT.Value  '输出查找结果
  Next matT
End Sub

运行过程,在立即窗口中输出匹配结果:

code.vba
1
2
3

二、替换

使用正则对象的Replace方法用指定字符串替换找到的内容。下面的代码中,对于给定的字符串"A1B2C3",找到所有非数字,替换为空,即删除它。

code.vba
Sub Test2()
  Dim objReg As New RegExp  '生成正则对象
  Dim strT As String
  strT = "A1B2C3"  '给定的字符串
  With objReg
    .Global = True  '全局替换
    .Pattern = "\D"  '查找非数字
    Debug.Print .Replace(strT, "")  '将找到的字符串替换为空,即删除
  End With
End Sub

运行过程,在立即窗口中输出替换后的结果。

code.vba
123

【Python】

在Python中,使用re模块提供的函数,可以直接用指定的正则表达式对给定文本进行字符串的搜索和替换,也可以通过创建正则表达式对象,然后使用该对象的属性和方法来实现。搜索的结果以Match对象的形式返回,可以利用该对象提供的属性和方法作进一步的显示和处理。

一、查找

re模块提供了4个函数来实现不同形式的查找功能,即match, search, findall和finditer函数,前两个函数返回一个满足要求的匹配对象,后两个返回所有满足要求的匹配对象。使用re模块,首先要导入该模块:

code.python
>>> import re

re.match函数

re.match函数从给定文本打头的位置开始匹配,如果匹配不成功,返回None。该函数的语法格式为:

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

其中pattern参数表示进行匹配的正则表达式,string参数表示给定的文本,flags参数指定标记,如表8-1所示。如果同时设置多个标记,标记之间可以用竖线连接,如re.M|re.I。

表8-1 标记设置

标记 完整写法 说 明
re.I re.IGNORECASE 不区分大小写
re.M re.MULTILINE 支持多行
re.S re.DOTALL 使点做任意匹配,包括换行符在内的任意字符
re.L re.LOCALE 进行本地化识别匹配
re.U re.UNICODE 根据Unicode字符集解析字符
re.X re.VERBOSE 支持更灵活更详细的模式,比如多行、忽略空白、加入注释等

如果匹配成功,re.match函数返回一个Match对象,否则返回None。

下面给定一个字符串和一个匹配规则,用re.match函数进行匹配。

code.python
>>> import re
>>> a="abc123def456"
>>> m=re.match("abc",a)
>>> m
<re.Match object; span=(0, 3), match='abc'>

从字符串a的打头位置开始匹配"abc",匹配成功,返回一个Match对象,它的值为"abc",位置是第1个字符到第3个字符。

如果给定的字符串和匹配字符串有大小写区分,例如:

code.python
>>> b="aBC123dEf456"
>>> m=re.match("abc",b)
>>> m

返回的m为空,表示匹配不成功。如果不区分大小写,使用re.I标记。

code.python
>>> m2=re.match(“abc”,b,re.I)
>>> m2
<re.Match object; span=(0, 3), match=’Abc’>

现在匹配成功,返回匹配结果"Abc"。

re.search函数

re.search函数与re.match函数不同,它在整个给定的字符串中进行查找,并返回第一个匹配成功的对象。该函数的语法格式与re.match函数的相同。

下面给定一个字符串,在整个字符串中查找"def",不区分大小写,返回查找到的第1个结果。

code.python
>>> import re
>>> a="aBC123dEf456"
>>> m=re.search("def",a,re.I)
>>> m
<re.Match object; span=(6, 9), match='dEf'>

匹配成功,返回匹配结果"dEf"。

re.findall函数

re.findall函数在给定字符串中查找正则表达式所匹配的所有子字符串,并将结果以列表的形式返回,如果匹配不成功,返回空列表。re.match和re.search函数只匹配一次,re.findall函数则找出所有匹配结果。re.findall函数的语法格式也与re.match函数的相同。

下面给定一个字符串,在整个字符串中查找"abc",不区分大小写,返回查找到的所有结果。

code.python
>>> import re
>>> a="aBC123dEf456abc789abC"
>>> m=re.findall("abc",a,re.I)
>>> m
['aBC', 'abc', 'abC']

可见,匹配成功的结果用列表的形式给出。

re.finditer函数

跟re.findall函数一样,re.finditer函数也是在给定字符串中找到正则表达式所匹配的所有子字符串。不同的是,前者将匹配结果以列表的形式给出,后者把匹配结果以迭代器的形式返回。该函数的语法格式与re.match函数的相同。

下面给定一个字符串,在整个字符串中查找"abc",不区分大小写,返回查找到的所有结果。

code.python
>>> import re
>>> a="aBC123dEf456abc789abC"
>>> m=re.finditer("abc",a,re.I)
>>> m
<callable_iterator object at 0x0000000005BF0F48>

可见,re.finditer函数是将匹配结果以迭代器的形式返回的。使用for循环可以输出迭代器中的对象。

code.python
>>> for i in m:
		print(i)
<re.Match object; span=(0, 3), match='aBC'>
<re.Match object; span=(12, 15), match='abc'>
<re.Match object; span=(18, 21), match='abC'>

可见,迭代器m中有3个匹配对象,for循环输出了它们的值和在给定字符串中的位置。

二、替换

所谓替换,是要在查找的基础上,用给定的对象替换匹配到的对象。使用re.sub和re.subn函数进行替换。

re.sub函数

函数的语法格式为:

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

其中各参数的意义为:

pattern - 进行匹配的正则表达式。

repl - 用作替换的字符串,可以是一个函数。

string – 给定的原始字符串。

count – 进行替换的最大次数,默认0表示全部替换。

flags - 指定正则表达式匹配方式的标记。

下面给定一个字符串,在整个字符串中查找"abc",不区分大小写,匹配结果全部替换为"xyz"。

code.python
>>> import re
>>> a="aBC123dEf456abc789abC"
>>> m=re.sub("abc","xyz",a,0,re.I)
>>> m
'xyz123dEf456xyz789xyz'

可见,所有匹配结果都被替换为"xyz"。

re.subn函数

re.subn函数的作用与re.sub函数的相同,只是该函数的返回值是一个元组,元组有两个值,第一个值为实现替换后的字符串,第二个值为进行替换的次数。

该函数的语法格式为:

code.python
subn(pattern, repl, string, count=0, flags=0)

其中各参数的意义与re.sub函数的相同,请参阅。

下面给定一个字符串,在整个字符串中查找"abc",不区分大小写,匹配结果全部替换为"xyz",使用re.subn函数。

code.python
>>> import re
>>> a="aBC123dEf456abc789abC"
>>> m=re.subn("abc","xyz",a,0,re.I)
>>> m
('xyz123dEf456xyz789xyz', 3)

试比较它与re.sub函数的返回值。

另外,使用re模块的re.compile函数可以创建Pattern对象,即编译好的正则表达式对象,利用它的属性和方法实现字符串的查找和替换。

下面给定原始字符串和正则表达式,用re.compile函数将正则表达式字符串编译为正则表达式对象,然后用表达式对象的match方法从原始字符串打头位置开始进行匹配。不区分大小写。

code.python
>>> import re
>>> a="aBc123def456"
>>> p=re.compile("abc",re.I)
>>> m=p.match(a)
>>> m
<re.Match object; span=(0, 3), match=’aBc’>

匹配成功,匹配子字符串为"aBc",匹配位置为第1个字符到第3个字符。[大谦Excel,dqexcel点com]