跳转到内容

Visual Basic/正则表达式

来自维基教科书,自由的教学读物

有时,内置的字符串函数不是解决当前问题的最方便或最优雅的方案。如果任务涉及操作复杂的字符模式,则正则表达式可能比简单的字符串函数序列更有效的工具。

Visual Basic 没有内置对正则表达式的支持。它可以通过 VBScript 正则表达式库使用正则表达式。如果您安装了 Internet Explorer,您几乎肯定有该库。要使用它,您必须将对项目的引用添加到项目;在项目菜单中选择引用,然后向下滚动到Microsoft VBScript 正则表达式。可能存在多个版本;如果是这样,请选择版本号最高的版本,除非您有特殊原因选择旧版本,例如与另一台机器上的该版本兼容。

类概述

[编辑 | 编辑源代码]

VBScript.RegExp 类的类概述

  • 属性
    • RegExp.Pattern
    • RegExp.Global
    • RegExp.IgnoreCase
    • RegExp.MultiLine
  • 方法
    • RegExp.Test
    • RegExp.Replace
    • RegExp.Execute

构建正则表达式

[编辑 | 编辑源代码]

构建正则表达式对象的方法

  Set Regexp = CreateObject("VBScript.RegExp")
  Regexp.Pattern = "[0-9][0-9]*"

构建正则表达式对象的方法,该方法要求在 Excel 中将引用设置为 Microsoft VBScript 正则表达式

  Set Regexp = new RegExp
  Regexp.Pattern = "[0-9][0-9]*"

测试匹配

[编辑 | 编辑源代码]

测试正则表达式的匹配示例

  Set RegExp = CreateObject("VBScript.RegExp")
  RegExp.Pattern = "[0-9][0-9]*"
  If RegExp.Test("354647") Then
   MsgBox "Test 1 passed."
  End If
  If RegExp.Test("a354647") Then
   MsgBox "Test 2 passed." 'This one passes, as the matching is not a whole-string one
  End If
  If RegExp.Test("abc") Then
   MsgBox "Test 3 passed." 'This one does not pass
  End If

测试匹配的示例,其中整个字符串必须匹配

  Set RegExp = CreateObject("VBScript.RegExp")
  RegExp.Pattern = "^[0-9][0-9]*$"
  If RegExp.Test("354647") Then
   MsgBox "Test 1 passed."
  End If
  If RegExp.Test("a354647") Then
   MsgBox "Test 2 passed." 'This one does not pass
  End If

查找匹配项

[编辑 | 编辑源代码]

遍历字符串中正则表达式所有匹配项集合的示例

  Set Regexp = CreateObject("VBScript.RegExp")
  Regexp.Pattern = "a.*?z"
  Regexp.Global = True 'Without global, only the first match is found
  Set Matches = Regex.Execute("aaz abz acz ad1z")
  For Each Match In Matches
   MsgBox "A match: " & Match
  Next

查找组

[编辑 | 编辑源代码]

访问匹配组的示例

  Set Regexp = CreateObject("VBScript.RegExp")
  Regexp.Pattern = "(a*) *(b*)"
  Regexp.Global = True
  Set Matches = Regexp.Execute("aaa bbb")
  For Each Match In Matches
   FirstGroup = Match.SubMatches(0) '=aaa
   SecondGroup = Match.SubMatches(1) '=bbb
  Next

将所有连字符序列替换为单个连字符的示例

  Set Regexp = CreateObject("VBScript.RegExp")
  Regexp.Pattern = "--*"
  Regexp.Global = True
  Result = Regexp.Replace("A-B--C----D", "-") '="A-B-C-D"

使用两种反向引用将重复字符串替换为其单一版本的示例

  Set Regexp = CreateObject("VBScript.RegExp")
  Regexp.Pattern = "(.*)\1"
  Regexp.Global = True
  Result = Regexp.Replace("hellohello", "$1") '="hello"

没有直接支持按正则表达式分割,但有一个解决方法。如果您可以假设分割字符串不包含 Chr(1),则可以先将分隔符正则表达式替换为 Chr(1),然后对 Chr(1) 使用非正则表达式分割函数。

按非零空格数分割的示例

 SplitString = "a b c  d"
 Set Regexp = CreateObject("VBScript.RegExp")
 Regexp.Pattern = " *"
 Regexp.Global = True
 Result = Regexp.Replace(SplitString , Chr(1))
 SplitArray = Split(Result, Chr(1))
 For Each Element In SplitArray
  MsgBox Element 
 Next

示例应用程序

[编辑 | 编辑源代码]

对于许多初学者来说,正则表达式背后的思想是如此陌生,以至于在讨论理论之前展示一个简单的示例可能是有价值的。给出的示例实际上是用于抓取网页以检索源代码的应用程序的开始,因此它也是相关的。

假设您需要解析一个网页以拾取主要标题及其引用的内容。这样的网页可能看起来像这样

文件:Visual Basic Classic RegExp 示例网页.png
 <html>
  <head>
   <title>RegEx Example</title>
  </head>
  <body>
   <h1>RegEx Example</h1>
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    <h2>Level Two in RegEx Example</h2>
     bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
   <h1>Level One</h1>
    cccccccccccccccccccccccccccccccccccccc
    <h2>Level Two in Level One</h2>
     dddddddddddddddddddddddddddddddddddd
  </body>
 </html>

我们要做的是提取两个h1 元素中的文本以及第一个h1和第二个h1之间的所有文本,以及第二个h1 元素和 body 结束标记之间的所有文本。

我们可以将结果存储在一个看起来像这样的数组中

"RegEx 示例" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n<h2>RegEx 示例中的二级标题</h2>\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
"一级标题" " cccccccccccccccccccccccccccccccccccccc\n<h2>一级标题中的二级标题</h2>\n dddddddddddddddddddddddddddddddddddd"

\n 字符序列代表行结束标记。它们可以是回车换行回车后跟换行

正则表达式指定要匹配的字符模式,匹配过程的结果是匹配整个表达式或表达式某些部分的子字符串列表。执行我们想要的表达式的表达式可能看起来像这样

 "<h1>\s*([\s\S]*?)\s*</h1>"
 

实际上它并没有完全做到,但它很接近。结果是在MatchCollection 类型的对象中的一系列匹配项。

 Item 0
  .FirstIndex:89
  .Length:24
  .Value:"<h1>RegEx Example</h1>"
  .SubMatches:
   .Count:1
   Item 0
    "RegEx Example"
 Item 1
  .FirstIndex:265
  .Length:20
  .Value:"<h1>Level One</h1>"
  .SubMatches:
   .Count:1
   Item 0
    "Level One"
 

项目的名称在每个项目的SubMatches 中,但文本在哪里?要获取它,我们可以简单地使用Mid$ 以及每个匹配项的FirstIndexLength 属性来查找一个h1 结束和下一个h1 开始之间的文本的开始和结束。但是,像往常一样,存在一个问题。最后一个匹配项不是以另一个h1 元素结束的,而是以body 结束标记结束的。因此,我们的最后一个匹配项将包括该标记以及可以在 body 之后的所有内容。解决方案是使用另一个表达式来先获取 body

"<body>([\s\S]*)</body>"

这仅返回一个匹配项,其中包含一个子匹配项,而子匹配项是 body 和 end body 标记之间的所有内容。现在,我们可以在这个新字符串上使用我们最初的表达式,它应该可以工作。

既然您已经看到了一个示例,这里是对使用的表达式的详细描述以及使用的正则表达式对象的属性设置。

正则表达式只是一个字符字符串,但某些字符具有特殊含义。在这个表达式中

"<body>([\s\S]*)</body>"

有三个主要部分

"<body>"
"([\s\S]*)"
"</body>"

这些部分中的每一个也是一个正则表达式。第一个和最后一个是简单的字符串,除了字符的标识之外没有其他含义,它们将匹配包含它们作为子字符串的任何字符串。

中间表达式有点模糊。它匹配任何字符序列,并且还捕获它匹配的内容。捕获由圆括号包围表达式来指示。捕获的文本将作为匹配项的SubMatches 之一返回。

<body> 仅匹配<body>
( 开始捕获表达式
[ 开始字符类
\s 指定包含所有空白字符的字符类
\S 指定包含所有非空白字符的字符类
] 结束字符类
* 表示要匹配尽可能多的先前表达式的实例
) 结束捕获表达式
</body> 匹配</body>

在本教材的案例研究部分,有一个简单的应用程序可用于测试正则表达式:正则表达式测试器.

[编辑 | 编辑源代码]
上一页:内置字符串函数 目录 下一页:数组
华夏公益教科书