這裡將介紹 Excel VBA 中各種字串的操作方式與相關的功能函數,並且提供各式常見的使用範例程式碼。
在使用 Excel VBA 處理資料時,通常除了數值的資料之外,文字的資料處理也是很常會遇到的工作,而相較於運算很單純的數值,文字的處理又更複雜了一些,例如轉換大小寫、取出部分的文字、比較或取代文字等,通常文字在處理時,都需要根據不同的問題來撰寫 VBA 程式碼,以下我們會說明各種常見的字串處理函數,並且提供各種常見的範例程式給大家參考。
字串的宣告與定義
VBA 中的 String 就是用來儲存字串(文字)的變數類型,其宣告與定義的方式跟一般的數值變數類似,而字串的內容在指定時要用雙引號包起來,例如:
Dim mystr As String
mystr = "This is a message!!!"
上面這兩行宣告了一個 mystr 字串變數,並且把該變數的內容設定為 This is a message!!!。
另外我們也可以利用 String 函數來建立重複字元的字串,例如:
Dim text1, text2 As String
text1 = String(6, "A") ' 產生 AAAAAA
text2 = String(3, 100) ' 產生 ddd
String 第一個參數是指定要產生的字串長度,而第二個參數則是指定重複的字元,除了直接用雙引號包起來的方式指定字元之外,也可以直接用 ASCII 的編碼指定,像這裡的 100 就是指 d 的 ASCII 編碼。
連接字串
如果要將多個字串連接起來,可以使用 & 這個運算子,以下是將三個字串連接在一起的範例。
Dim text1 As String, text2 As String
text1 = "Hello"
text2 = "World"
' 使用 & 連接字串
MsgBox text1 & ", " & text2
這裡我們定義了兩個字串變數 text1 與 text2,接著使用 & 把它們接在一起,並且在兩個英文單字之間加入一個逗號與空白。

執行之後,就會輸出三個字串連接起來的結果。

字串長度
在 VBA 中若要取得一個字串的長度,可以使用 Len 函數:
MsgBox Len("Hello, world.")
Len 函數的參數只有一個字串變數,呼叫 Len 之後它就會傳回該字串的長度:


Len 傳回的數值是一個整數,我們也可以把它儲存在變數中做其他的運算:
Dim text As String
text = "Hello World"
Dim textLen As Integer
textLen = Len(text)
MsgBox "textLen = " & textLen


搜尋字串位置
若要尋找一個字串中某個子字串的位置,可以使用 InStr 函數:
Dim pos As Integer
pos = InStr("Hello, world.", "world")
MsgBox "pos = " & pos
InStr 第一個參數是放一個比較長的字串,而第二個參數則是放要搜尋的關鍵字,以這個例子來說它就會在 Hello, world. 這一個字串中尋找 world 出現的位置。


InStr 預設在搜尋時,英文的大小寫是視為不同的,若希望不分大小寫,可將比對方式參數設定為 vbTextCompare:
Dim pos As Integer
pos = InStr(1, "Hello, World.", "world", vbTextCompare)
MsgBox "pos = " & pos
這裡的第一個參數是用來指定搜尋的起始位置,在上面的範例中我們將這個參數省略掉了,在這裡使用比對方式參數時就要加進來,這樣執行之後 InStr 在比對字串時就會將英文字母的大小寫視為相同的。
在 Excel VBA 程式碼編輯器中輸入函數時,通常都會有這樣的參數用法提示。

有些函數的參數非常多,若是忘記參數的詳細位置與順序時,就可以善用這個小技巧。
如果想從字串的結尾處開始往左搜尋,則可改用 InStrRev:
Dim pos As Integer
pos = InStrRev("Hello, World.", "l")
MsgBox "pos = " & pos

若要讓 InStrRev 搜尋時不分大小寫,可以這樣寫:
Dim pos As Integer
pos = InStrRev("Hello, WORLD.", "l", -1, vbTextCompare)
MsgBox "pos = " & pos
第三個參數是搜尋起始點,預設值是 -1(從最右方開始搜尋),最後一個參數就是比對方式,用法同上。
取出子字串
如果要從一段字串中取出部分的子字串,依據不同的狀況會有好幾種作法,以下是各種取出子字串的函數與用法。
開頭子字串
如果想要取出一段文字中開頭的部分,可以使用 Left 函數:
MsgBox Left("Hello, world.", 5)
Left 的意思就是從左邊開始擷取子字串的意思,第一個參數是完整的字串,而第二個參數則是要擷取的子字串長度,執行結果如下:

結尾子字串
如果想要取出一段文字中結尾的部分,可以使用 Right 函數,其用法跟 Left 類似,只是換成從右邊開始擷取子字串而已:
MsgBox Right("Hello, world.", 6)
執行結果如下:

任意位置子字串
如果要擷取的子字串位置不在文字的兩端,而是在文字的中間,則可使用 Mid 函數:
MsgBox Mid("This is a message.", 6, 2)
Mid 函數的第一個參數是完整的字串,第二個參數是要擷取的子字串位置(從左邊算起),而第三個參數則是要擷取的子字串長度,這個例子就會從 This is a message. 這段文字的第 6 個字元開始擷取,取出 2 個字元,執行結果如下。

如果使用 Mid 時不指定擷取的字串長度,則 Mid 就會從指定的位置開始擷取直到整個字串的結尾,例如:
MsgBox Mid("This is a message.", 6)

移除多餘的空白字元
如果在字串中除了主要的文字資料之外,前方還包含了多餘的空白字元,這時候就可以使用 Trim 系列的函數將多餘的空白移除。
移除開頭空白字元
LTrim 函數可將字串左邊的空白移除:
Dim mystr As String
mystr = " Hello, world."
MsgBox "After LTrim : " & LTrim(mystr)
LTrim 會自動判斷字串開頭的空白字元長度,將左邊開頭所有的空白都刪除,執行結果為:

移除結尾空白字元
如果空白位於字串結尾處,則可使用 RTrim,其用法與 LTrim 類似:
Dim mystr As String
mystr = "Hello, world. "
MsgBox "After RTrim : " & RTrim(mystr)
結尾的空白移除之後,執行結果與上面相同。
移除開頭與結尾空白字元
如果要同時將前後兩端的所有空白字元都刪除,可以使用 Trim 函數:
Dim mystr As String
mystr = " Hello, world. "
MsgBox "After Trim : " & Trim(mystr)
開頭與結尾的空白移除之後,執行結果與上面相同。
產生空白字元
如果需要特定長度的空白字串時,可以用 Space 來產生,例如:
MsgBox ("Hello," & Space(10) & "world.")

字串取代
Replace 函數可以將字串中的指定的文字替換成其他的文字,這個函數的完整參數用法如下:
Replace(字串, 搜尋文字, 替換文字[, 起始位置[, 替換次數[, 比對方式]]])
最簡單的用法就是單純將字串中指定的文字替換掉:
Dim mystr As String
mystr = "This is a message."
newstr = Replace(mystr, "message", "dog")
MsgBox "After Replace : " & newstr
這裡是將 mystr 中的 message 替換為 dog,結果如下:

如果指定要替換的文字在整個字串中有出現好多次,Replace 預設會全部替換掉:
Dim mystr As String
mystr = "This is a message."
newstr = Replace(mystr, "is", "**")
MsgBox "After Replace : " & newstr
這裡就會將字串中的兩個 is 都替換為 **,結果如下:

如果只想要替換特定位置的文字,或是限制替換次數,可以搭配起始位置與替換次數兩個參數的方式來處理:
Dim mystr As String
mystr = "This is a message."
newstr = Replace(mystr, "is", "**", 1, 1)
MsgBox "After Replace : " & newstr
這樣 Replace 就會從字串的開頭開始搜尋,只替換第一個比對成功的文字。

如果要替換比較後面的文字,可以調整起始位置參數,不過 Replace 會自動將起始位置之前的文字截斷:
Dim mystr As String
mystr = "This is a message."
newstr = Replace(mystr, "is", "**", 5, 1)
MsgBox "After Replace : " & newstr
執行結果如下:

在預設的狀況下 Replace 會將英文字母的大小寫視為不同的字串:
Dim mystr As String
mystr = "This Is a Message."
newstr = Replace(mystr, "is", "**")
MsgBox "After Replace : " & newstr

如果要讓 Replace 在比對時,不要區分英文字母的大小寫(大小寫視為相同),可以將比對方式參數指定為 vbTextCompare:
Dim mystr As String
mystr = "This Is a Message."
newstr = Replace(mystr, "is", "**", 1, -1, vbTextCompare)
MsgBox "After Replace : " & newstr
由於比對方式的參數是最後一個,所以在使用時要把前面的起始位置與替換次數兩個參數也都寫進去,這裡的替換次數設定為 -1 是代表不限制的意思,而比對方式預設值是 vbBinaryCompare(大小寫視為不同),這裡改為 vbTextCompare 之後,就可以同時比對字串中大寫與小寫的文字。

字串比較
StrComp 可以比較不同字串之間的大小差異,在排序資料時常常會用到,其參數用法如下:
StrComp(字串一, 字串二[, 比對方式])
前兩個參數就是兩個要比較的字串,而第三個比對方式參數可用來指定是否區分大小寫,預設值是 vbBinaryCompare(大小寫視為不同),若設定為 vbTextCompare 則會將大小寫視為相同。
而 StrComp 在比較兩個字串之後,會傳回不同的數值來代表不同的結果:
| 狀況 | StrComp 傳回數值 |
|---|---|
字串一 < 字串二 | -1 |
字串一 = 字串二 | 0 |
字串一 > 字串二 | 1 |
以下是一些比較的範例:
MsgBox StrComp("Hello", "Hello") ' 結果為 0
MsgBox StrComp("Hello", "HELLO") ' 結果為 1
MsgBox StrComp("Hello", "hello") ' 結果為 -1
MsgBox StrComp("Hello", "hello", vbTextCompare) ' 結果為 0
字串反轉
StrReverse 可將字串反轉:
MsgBox StrReverse("Hello, world.")

大小寫轉換
UCase 與 LCase 可以將字串中的英文字母轉換為大寫或小寫:
MsgBox UCase("Hello, world.") ' HELLO, WORLD.
MsgBox LCase("Hello, world.") ' hello, world.
常見範例
從學號產生 Email 位址
假設我們在 Excel 檔有一系列的學號,而各個學生的 Email 位址都是依據學號來建立的。

這樣我們就可以利用簡單的 VBA 程式將學號轉換成 Email 位址:
Dim sid, email As String
For i = 2 To 7
sid = Cells(i, 1)
email = sid & "@your.domain.com"
Cells(i, 2) = email
Next i
執行的結果如下:

從 Email 取出學號
假設我們有一些固定格式的 Email 位址,而這些 Email 都是以一個英文字母 u 開頭,加上學生的學號組成的,實際資料如下:

若要從中取出學號的部分(也就是數字的部分),可以這樣寫:
Dim sid, email As String
For i = 2 To 7
email = Cells(i, 1)
sid = Mid(email, 2, 7)
Cells(i, 2) = sid
Next i
執行的結果如下:

房屋實價登錄資料
這是從政府資料開放平台上下載的房屋實價登錄資料。

假設我要找出所有大安區信義路上房屋的平均單價,就可以使用一個 For 迴圈配合 InStr 來搜尋:
Dim street, price As String
Dim s As Double
Dim cnt As Integer
s = 0
cnt = 0
For i = 2 To 948
street = Cells(i, 2) ' 門牌
' 檢查門牌是否包含「大安區信義路」
If InStr(street, "大安區信義路") > Then
price = Cells(i, 5) ' 單價
If Len(price) > 0 Then ' 跳過沒有單價的資料
cnt = cnt + 1 ' 計算建物總數
s = s + Val(price) ' 計算建物單價總和
End If
End If
Next i
MsgBox "建物總數 = " & cnt & vbCrLf _
& "平均單價 = " & Round(s / cnt)
這裡我們以一個 For 迴圈把每一筆房屋成交紀錄都跑過一次,把每一筆資料的門牌地址抓出來使用 InStr 比對,如果有包含大安區信義路 的字樣,就把該建物的單價記錄下來,最後即可求得整體的平均單價。
由於有些資料的單價欄位是空白的,所以我們在使用單價欄位資料之前還要先以 Len 來檢查一下,確認這個欄位確實有資料之後,才將該筆資料納入計算。
這裡的 Val 函數的用途是將字串資料轉換為數值,而輸出時所加入的 vbCrLf 是指換行的意思。

這個範例的資料與 VBA 程式我放在這個 Excel 檔,有興趣的人可以下載回去執行。
這裡我們只是拿房屋實價登錄資料來示範字串處理的應用方式,實際上房屋的價格評估當然沒有那麼單純,真正的重點在於熟練各種字串處理的相關函數,並結合各種迴圈與判斷結構,練習針對類型的文字資料撰寫對應的 VBA 程式碼。
