VBS脚本编程(9)——正则表达式

正则表达式介绍

正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表达式、常规表达式,在计算机科学中,是指一个用描述或者匹配一系列符合某个句法规则的字符串的单个字符串。在很多文本编辑器或其他工具里,正则表达式通常被用来检索和/或替换那些符合某个模式的文本内容。许多程序设计语言都支持利用正则表达式进行字符操作。
 
正则表达式提供了强大的字符匹配和替换功能。在 VBScript 引擎增加正则表达式之前,需要很多代码,包括循环、InStr 和 Mid 函数,才能实现对字符串的查找和替换。现在有了正则表达式,只要一行代码就能实现这些。
如果您以前使用过其他语言(C#、C++、Perl、awk 或 JavaScript—— 甚至微软自己的 Jscript 都在 VBScript 之前提供了对正则表达式的支持),正则表达式对您来说可能就不新鲜了。但有经验的程序员在 VBScript 中使用正则表达式时也要注意 VBScript 并不支持正则表达式常量(就像/a pattern/),而是将文本字符串赋给 RegExp 对象的 Pattern 属性。很多情况下这都比传统的方法好,因为无需学习新的语法。但是如果您曾经使用过其他语言中的正则表达式,尤其是客户端 JavaScript,这会让您感到意外。
现在很多 Windows 平台的文本编辑器也开始模仿 UNIX 文本编辑器 vi 提供对正则表达式搜索的支持。其中包括 UltraEdit-32(www.ultraedit.com)和 SlickEdit(www.slickedit.com)。
 
 

正则表达式的简单实战例子-1

熟悉正则表达式的最好、最快的途径就是看例子。下面这个例子可能是最简单的正则表达式例子,涉及到查找和替换。
Dim re,s
Set re = New RegExp
re.Pattern = "France"
s = "The rain in France falls mainly on the plains"
MsgBox re.Replace(s,"Spain")
没什么特别的—— 但这为以后的应用打下了很好的基础。下面来看看这段代码的原理。
1. 新建一个正则表达式对象。
Set re = New RegExp
2. 设置这个对象的关键属性。这就是需要匹配的模式。
re.Pattern = "France"
3. 接下来这一行是需要搜索的字符串:
s = "The rain in France falls mainly on the plains."
这一行是这个脚本真正的能力源泉。它要求正则表达式查找这个变量中最早出现的"France"(模式),并将其替换成"Spain"。
4. 完成之后,用消息框展示强大的查找和替换技能。
MsgBox re.Replace(s, "Spain")
5. 脚本运行的最终输出如图
现在这个脚本一开始就固定了字符串和查找的标准,但是也可以从外部输入获取字符串和查找替换标准,使这个脚本更加灵活。
Dim re, s, sc
Set re = New RegExp
s = InputBox("Type a string for the code to search")
re.Pattern = InputBox("Type in a pattern to find")
sc = InputBox("Type in a string to replace the pattern")
MsgBox re.Replace(s, sc)
这跟前面的代码基本上是一样的,主要的区别在于没有将所有的功能都固化在脚本内,而是通过代码中的三个输入框增加了脚本的灵活性。
s = InputBox("Type a string for the code to search")
re.Pattern = InputBox("Type in a pattern to find")
sc = InputBox("Type in a string to replace the pattern")
最终的修改是在最后一行代码中 Replace 方法使用了 sc 变量。
MsgBox re.Replace(s, sc)
这样就可以手动输入需要搜索的字符串
然后输入需要查找的模式
最后输入一个用来替换模式的字符串
如果试图查找和替换字符串中不存在的模式会怎么样?实际上什么都不会发生
 

正则表达式的简单实战例子-2

显然,您之前所看到的例子都很简单,但是老实地说,只需要 VBScript 的字符串操作函数就能实现这些功能。但如果要替换字符串中出现的所有模式怎么办?或是要替换字符串中出现的所有的模式,除了出现在单词结尾的字符串?需要调整一下这段代码。看一下接下来的代码:
Dim re,s
Set re = New RegExp
re.Pattern = "\bin"
re.Global = True
s = "The rain in Spain falls mainly on the plains."
MsgBox re.Replace(s,"in the country of")
这个版本有两处关键的改变:
● 用特殊序列(\b)匹配单词边界
假设这里没有\b,就像这样:
Dim re,s
Set re = New RegExp
re.Pattern = "in"
re.Global = True
s = "The rain in Spain falls mainly on the plains."
MsgBox re.Replace(s,"in the country of")
没有了“\b”,单词“rain”、“Spain”、“mainly”和“plains”中的“in”也都会被替换成“in the country of”。如图
● 通过设置 Global 属性,确保匹配到所有您想要匹配到的"in"。
正则表达式提供了一种强大的语言来实现复杂的模式匹配,让我们继续学习能让您在 VBScript 中使用正则表达式的对象。
 

RegExp对象

RegExp 对象是 VBScript 中用于提供简单的正则表达式支持的对象。VBScript 中所有和正则表达式有关的属性和方法都与这个对象有关联。
Dim re
Set re = New RegExp
这个对象有三个属性和三个方法,如表所示。
属 性
Global 属性
IgnoreCase 属性
Pattern 属性
方 法
Execute 方法
Replace 方法
Test 方法
接下来的几节会深入地介绍这些属性和方法。此外还会介绍您将在模式中用到的正则表达式符号。
 

RegExp对象的属性

1、Global属性
Global属性负责设置返回一个Boolean值,指明模式是匹配整个字符串中所有与之相符的地方还是只匹配第一次出现的地方
代码
object.Global [= value ]
对象
RegExp 对象
有两个可能的值:True 和 False
如果 Global 属性的值是 True,那就会对整个字符串进行查找;否则就不会。
默认值是 False。
 
2、IgnoreCase属性
IgnoreCase 属性负责设置或返回一个 Boolean 值,指明模式匹配是否大小写敏感
代码
object.Global [= value ]
对象
RegExp 对象
有两个可能的值:True 和 False
如果 IgnoreCase 属性的值为 False,搜索为大小写敏感;如果是 True,则不是。
默认是 False。
 
3、Pattern属性
Pattern 属性设置或返回用于搜索的正则表达式。
前面所有的例子都用到了 Pattern。
代码
object.Pattern [= "searchstring"]
对象
RegExp 对象
需要搜索的正则字符串表达式。可能含有一些正则表达式字符
—— 可选的。
 
 

正则表达式字符

正则表达式的强大并不是来自于用字符串做模式,而是在模式中使用特殊字符。下表列出了所有的这些字符,以及每个字符在代码中的作用。
大写特殊字符的作用与相应的小写特殊字符的作用相反。
字 符
描 述
\
表示下一个字符特殊字符或文字常量
^
匹配输入的开头
$
匹配输入的结尾
*
匹配前一个字符零次或多次
+
匹配前一个字符一次或多次
?
匹配前一个字符零次或一次
.
匹配换行符以外的任何字符
(pattern)
匹配并记住这个模式。可以用[0]...[n]从结果的matches集合中获取匹配到的字符串。要匹配括号本身,在前面加上斜杠——"(" 或 ")"
(?:pattern)
匹配但不捕获模式,也就是不会存储到匹配结果供以后使用。这可以用于使用"or"字符(|)合并模式的不同部分。
例如,"anomal(?:y|ies)"比"anomaly|anomalies"要划算很多
(?=pattern)
当所有搜索的字符串匹配了模式的开头部分时就接着匹配这一部分。这是一个非捕获匹配,也就是说不会保存匹配结果供以后使用。
例如,"Windows(?=95|98|NT|2000|XP|Vista)"能匹配"Windows Vista"中的 Windows 而不
能匹配"Windows 3.1"中的 Windows
(?!pattern)
与上一个相反,这会匹配模式中没有的内容。这是一个非捕获匹配,也就是说不会保存匹配结果供以后使用。
例如,"Windows
(?=95|98|NT|2000|XP|Vista)"能匹配"Windows 3.1"中的 Windows 而不能
匹配"Windows Vista"中的 Windows
x|y
匹配x或y,
x的优先级大于y
{n}
准确匹配n次(n必须是一个非负数)
{n,}
至少匹配n次(n必须是一个非负数)
{n,m}
至少匹配n次,最多匹配m次(n和m必须是一个非负数)
[xyz]
匹配其中包括的任一个字符(xyz表示一个字符集)
[^xyz]
匹配其中不包括的字符(^xyz表示一个字符集的补集)
[a-z]
匹配指定范围内的字符(a-z表示字符的范围)
[^m-z]
匹配指定范围外的字符(^m-z表示指定范围的补集)
\b
匹配一个单词边界,这个位置的单词在空格之间
\B
匹配一个非单词边界
\d
匹配数字。等价于[0-9]
\D
匹配非数字。等价于[^0-9]
\f
匹配换页符
\n
匹配换行符
\r
匹配回车符
\s
匹配空白,包括空格、制表符、换页符等。等价于"[\f \n \r \t \v ]"
\S
匹配非空白字符。等价于"[^\f \n \r \t \v ]"
\t
匹配制表符
\v
匹配纵向制表符
\w
匹配字母、数字,以及下划线。等价于"[A-Za-z0-9_]"
\W
匹配非字符数组。等价于"[^A-Za-z0-9\_]"
\.
匹配.
\|
匹配|
\{
匹配{
\}
匹配}
\\
匹配\
\[
匹配[
\]
匹配]
\(
匹配(
\)
匹配)
$num
匹配num,其中num是正整数,返回匹配结果的引用
\n
匹配n,其中n是八进制转义符
\uxxxx
匹配Unicode形式的ASCII
\xn
匹配n,其中n是十六进制转义符。十六进制转义符必须是两位长度
 
1、匹配一类字符
您已经见过一个简单的模式:
re.Pattern = "in"
它通常用来匹配一类字符。通过将需要匹配的字符放在方括号中就能实现。例如,下面这个例子将单个数字换成更通用的术语。
Dim re,s
Set re = New RegExp
re.Pattern = "[23456789]"
s = "Spain received 3 millimeters of rain last week."
MsgBox re.Replace(s,"many")
在这个例子中,数字“3”被替换成了文本“many”。正如您所期望的,可以指定一个范围来缩短这个模式。这个模式跟前一个的功能完全一样。
Dim re,s
Set re = New RegExp
re.Pattern = "[2-9]"
s = "Spain received 3 millimeters of rain last week."
MsgBox re.Replace(s,"many")
 
2、替换数字和非数字
经常需要替换数字。实际上,由于经常要用到模式[0-9](包括所有数字),所以有一种[0-9]的等价快捷方式:\d。
Dim re,s
Set re = New RegExp
re.Pattern = ("\d")
s = "a b c d e f 1 g 2 h "
re.Global = True
MsgBox re.Replace(s,"number")
如果要匹配非数字的字符怎么办?在方括号中使用^符号。
在方括号外使用^的意义就完全不同了,稍后会对此做讨论。
这样就可以使用下面的模式匹配非数字的字符:
① re.Pattern = "[^,0-9]" 'the hard way
② re.Pattern = "[^\d]" 'a little shorter
③ re.Pattern = "[\D]" 'another of those special characters
最后一个模式使用了另一个特殊字符。在大部分情况下这种特殊字符只是减少了您的输入次数 (或是一种有效的记忆方法),但在有些情况下,比如遇到匹配制表符和其他不能打印的字符时,这就很有用了。
 
3、锚定和缩短模式
三种特殊字符用于锚定模式。它们本身不匹配任何字符,但是可以要求另一个模式必须出现在输入的开头(在[]外使用^)、输入的结尾($)或是单词边界(您已经见过的\b)。
另一种缩短模式的方法是使用重复数。基本的思路就是在模式后面指定重复的次数。例如,下面这个模式,如图所示,可以匹配多个数字并替换它们。
Dim re, s
Set re = New RegExp
re.Pattern = "\d{3}"
s = "Spain received 100 millimeters of rain in the last 2 weeks."
MsgBox re.Replace(s, "a whopping number of")
如果不在代码中使用重复数,它会留下最后字符串中的"00"。
 
4、指定匹配的范围或最小次数
前面的表已经匹配罗列了,还可以指定匹配的最小次数{min}或范围{min,max}。其中一些常用的重复模式也有专门的快捷方式。
① re.Pattern = "\d+" '1个及以上的重复数,相当于 "\d{1, }"
② re.Pattern = "\d*" '0个及以上的重复数,相当于 "\d{0, }
re.Pattern = "\d?" '0个或1个重复数,相当于 "\d{0,1}"
 
Dim re,s
Set re = New RegExp
re.Global = True
re.Pattern = "\d+"
s = "Spain received 100 millimeters of rain in the last 2 weeks."
MsgBox re.Replace(s,"a number")
这段代码的输出如图,注意字符串“100”和"2"被替换了。
 
Dim re,s
Set re = New RegExp
re.Global = True
re.Pattern = "\d*"
s = "Spain received 100 millimeters of rain in the last 2 weeks."
MsgBox re.Replace(s,"a number")
上面这段代码的输出如图,这里在每两个非数字的字符间插入了这个字符串,而数字则被替换了。
 
5、记住匹配结果
最后一个要讨论的特殊字符是记住匹配的结果。如果要在用于替换的文本中使用部分或全部的匹配结果,这就很有用——详见 Replace方法,其中一个例子使用了记住匹配的结果。
为了验证这一点,也为了将所有关于特殊字符的讨论集中在一起,我们来做点有实际意义的事情。搜索一个字符串,查找其中的URL。为了控制这个例子的复杂度和规模,这里只查找其中的 "http:"协议,但是您还可以处理各种DNS域名,包括无限的域名层次。不要担心任何与DNS交流,只需要知道在浏览器中输入URL就够了。
下一节中关于另一个RegExp对象方法的代码中会有更多的细节信息。现在,只需要知道Excute会执行模式匹配并同过集合返回各个匹配结果。这里是代码:
Dim re, s
Set re = New RegExp
re.Global = True
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
s = "http://www.kingsley-hughes.com is a valid web address. And so is "
s = s & vbCrLf & "http://www.wrox.com. And "
s = s & vbCrLf & "http://www.pc.ibm.com - even with 4 levels."
Set colMatches = re.Execute(s)
For Each match In colMatches
MsgBox "Found valid URL: " & match.Value
Next
如您所愿,其中主要的工作就是设置模式的那一行代码。看上去有点让人生畏,但实际上很容易理解。让我们将其分解开来:
1. 模式以固定的字符串 http://开头。然后用圆括号将模式的主要部分括起来。下面高亮的模式会匹配一级 DNS,包括尾部的点:
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
这个模式以一个您之前见过的特殊字符\w 开头,用来匹配[a-zA-Z0-9],也就是英语中的所有数字和字母。
2. 用括号匹配字母数字或横杠,因为 DNS 中可以有横杠。为什么不使用与前面一样的模式?很简单,因为有效的 DNS 不能以横杠开始或结尾。然后用*重复匹配 0 个或多个字符。
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
3. 然后又严格地用字母数字,这样域名就不会以横杠结束。括号中的最后一个模式匹配用于分割 DNS 层次的点(.)。
不能单独使用点,因为那是一个特殊字符,正常情况下能匹配除换行符以外的任何字符。可以用反斜杠转义这个字符。
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
4. 在将这些东西封装到括号中之后,只需要继续使用*重复这个模式即可。所以下面高亮显示的模式可以匹配所有有效的域名以及其后的点。换句话说就是能匹配整个 DNS 中的一级域名。
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
5. 模式最后是顶级域名(比如 com、org、edu 等)所需的一个或多个字符。
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
 
 

RegExp对象的方法

1、Execute方法
这个方法将正则表达式应用到字符串上并返回Matches集合。这是代码中使用模式匹配字符串开关,使用方法详见下表:
代码
object.Execute( string )
对象
只能是 RegExp 对象
字符串
需要搜索的字符串—— 必需的
正则表达式搜索的模式就是 RegExp 对象的 Pattern 属性。
要注意,有些语言对正则表达式结果的处理方式不一样,其 Execute 返回的是判断模式是否找到的布尔值。这种差异导致您经常会看到从其他语言中转换过来的正则表达式在 VBScript 中无法使用。
有些微软的文档也含有这类错误,不过其中大部分都已经改正了。
记住 Execute 的结果是一个集合,甚至很有可能是一个空集合。可以用 if re.Execute(s).
count = 0 或专门为这个目的设计的 Test 方法来测试它。
 
2、Replace方法
这个方法用于替换在正则表达式搜索中找到的文本,用法参见下表。
代码
object.Replace(string1, string2)
对象
只能是 RegExp 对象
字符串 1
这是发生替换的文本字符串—— 必需的
字符串 2
这是用于替换的文本字符串—— 必需的
Replace 方法返回一份 RegExp.Pattern 被 string2 替换后的 string1 的副本。如果字符串中没有发生匹配,那么就会返回没有任何改变的 string1。
Replace 方法还可以替换模式中的子表达式。这需要在用于替换的文本中使用特殊字符$1、$2 等。这些“参数”就是记住的匹配结果
 
3、Backreferencing——反向引用
一个被记住的匹配结果就是模式的一部分。这就是所谓的Backreferencing,也称反向引用或回溯引用。需要用圆括号指定需要存储在临时缓存中的部分。每个捕获到的匹配结果都会被按匹配到的先后次序存放 (在正则表达式模式中从左到右)。存放匹配结果的缓存从1开始编号,最大可以到99。可以依次使用$1、$2之类的变量访问它们。
可以用非捕获元字符("?:"、"?="或"?!")跳过正则表达式的某些部分。
接下来的例子,前 5 个单词(由一个或多个非空白字符组成)会被记住,然后只有其中的4 个会出现在替换文本中:
Dim re, s
Set re = New RegExp
re.Pattern = "(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)"
s = "VBScript is not very cool."
MsgBox re.Replace(s, "$1 $2 $4 $5")
注意这段代码中为字符串中的每个代词都添加了一对(\S+)\s+。这使得代码能更好地控制所要处理的字符串。可以阻止字符串的尾部被添加到要显示的字符串中。要注意在使用 backreferencing 时要确保输出符合您的要求!
 
3、Test方法
Test 方法对字符串执行正则表达式搜索,并返回一个布尔值说明匹配是否成功,用法见下表:
代码
object.Test(string)
对象
RegExp 对象
字符串
正则表达式搜索的执行对象—— 必需的
如果匹配成功,Test 方法返回 True;否则返回 False。这适用于判断字符串是否含有某个模式。注意,常常需要将模式设为大小写敏感,就像下面这个例子:
Dim re,s
Set re = New RegExp
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
s = "Some long string with http://www.wrox.com buried in it."
If re.Test(s) Then
MsgBox "Found a URL"
Else
MsgBox "Not URL found"
End if 
 

Matches集合

1、Matches集合
Matches集合含有正则表达式的Match对象。
只有使用RegExp对象的Execute方法才能创建这个集合。要记住Matches集合的属性跟独立的Match对象的属性一样,都是只读的。
当一个正则表达式被执行时,会产生0个或多个Match对象。每个Match对象提供一下三个内容:
① 正则表达式所找到的字符
② 字符串的长度
③ 指向找到该匹配位置的索引
要记得将Global属性设置为True,否则您的Matches集合中最多也只会有一个成员。这种方法很简单,但是很难调试。
Dim re,objMatch,colMatches,sMsg
Set re = New RegExp
re.Global = True
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
s = "http://www.kingsley-hughes.com is a valid web address. And so is "
s = s & vbCrLf & "http://www.wrox.com. As is "
s = s & vbCrLf & "http://www.wiley.com."
Set colMatches = re.Execute(s)
sMsg = ""
For Each objMatch In colMatches
sMsg = sMsg  & objMatch.Value
sMsg = sMsg & ", found at position " & objMatch.FirstIndex & " of the string."
sMsg = sMsg & "The length matched is "
sMsg = sMsg & objMatch.Length & "." & vbCrLf
Next
MsgBox sMsg
 
2、Matches的属性
Matches 是一个简单的集合,只有两个属性:
1. Count 返回集合中的元素数量。
Dim re,objMatch,colMatches
Set re = New RegExp
re.Global = True
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
s = "http://www.kingsley-hughes.com is a valid web address. And so is "
s = s & vbCrLf & "http://www.wrox.com. As is "
s = s & vbCrLf & "http://www.wiley.com."
Set colMatches = re.Execute(s)
MsgBox colMatches.Count
这段代码的输出如图所示。
 
2. Item 根据指定的键返回元素
Dim re,objMatch,colMatches
Set re = New RegExp
re.Global = True
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
s = "http://www.kingsley-hughes.com is a valid web address. And so is "
s = s & vbCrLf & "http://www.wrox.com. As is "
s = s & vbCrLf & "http://www.wiley.com."
Set colMatches = re.Execute(s)
MsgBox colMatches.Item(0)
MsgBox colMatches.Item(1)
MsgBox colMatches.Item(2)
 

Match对象

Match 对象是 Matches 集合中的成员。创建 Match 对象的唯一方法就是使用 RegExp 对象的 Execute 方法。当一个正则表达式被执行时会产生 0 个或多个 Match 对象。每个 Match对象提供以下内容:
① 正则表达式所找到的字符串
② 字符串的长度
③ 指向找到该匹配的位置的索引
Match 的三个属性都是只读的:Value、Length、FirstIndex 以及 。接下来会详细介绍这些属性。
 
1、Value属性
Value 属性返回在字符串中找到的匹配结果的值或文本,用法见下表:
代码
object.Value
对象
只能是 Match 对象
 
2、Length属性
Length 属性返回在字符串中找到的匹配的长度,用法见下表:
代码
object.Length
对象
只能是 Match 对象
 
3、FirstIndex属性
FirstIndex属性返回匹配结果在字符串中的位置,用法见下表:
代码
object.FirstIndex
对象
只能是 Match 对象
FirstIndex 属性从 0 开始对被搜索的字符串编号。换句话说就是字符串中的第一个字符就是字符 0。
Dim re,objMatch,colMatches,sMsg
Set re = New RegExp
re.Global = True
re.Pattern = "http://(\w+[\w-]*\w+\.)*\w+"
s = "http://www.kingsley-hughes.com is a valid web address. And so is "
s = s & vbCrLf & "http://www.wrox.com. As is "
s = s & vbCrLf & "http://www.wiley.com."
Set colMatches = re.Execute(s)
sMsg = ""
For Each objMatch In colMatches
sMsg = sMsg  & objMatch.Value & "----"
sMsg = sMsg  & objMatch.FirstIndex & "----"
sMsg = sMsg & objMatch.Length & vbCrLf
Next
MsgBox sMsg
这段代码的输出如图所示。
posted @ 2021-06-24 15:03  Ulysses~  阅读(1274)  评论(0编辑  收藏  举报