跳轉到內容

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>

在本教材的案例研究部分,有一個簡單的應用程式可用於測試正則表示式:正則表示式測試器.

[編輯 | 編輯原始碼]
上一頁:內建字串函式 目錄 下一頁:陣列
華夏公益教科書