Lua 的模式匹配不使用Posix規范的正則表達式(也寫做regexp)來進行模式匹配。主要的原因出于程序大小方面的考慮:實現一個典型的符合POSIX標準的regexp大概需要4000行代碼,這比整個Lua標準庫加在一起都大。權衡之下,Lua中的模式匹配的實現只用了500行代碼,當然這意味著不可能實現POSIX所規范的所有更能。然而,Lua中的模式匹配功能是很強大的,并且包含了一些使用標準POSIX模式匹配不容易實現的功能。
Lua支持的所有字符類:
. 任意字符
%a 字母
%c 控制字符
%d 數字
%l 小寫字母
%p 標點字符
% s 空白符
%u 大寫字符
%w 字母和數字
%x 十六進制數字
%z 代表0的字符
上面字符類的大寫形式表示小寫所代表的集合的補集。例如,'%A'非字母的字符:
print(string.gsub("hello, up-down!", "%A", "."))
--> hello..up.down. 4
例子:
s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date))) --> 30/05/1999
在模式匹配中有一些特殊字符,他們有特殊的意義,Lua中的特殊字符如下:
( ) . % + - * ? [ ^ $
'%' 用作特殊字符的轉義字符,因此 '%.' 匹配點;'%%' 匹配字符 '%'。轉義字符 '%'不僅可以用來轉義特殊字符,還可以用于所有的非字母的字符。當對一個字符有疑問的時候,為安全起見請使用轉義字符轉義他。
對Lua而言,模式串就是普通的字符串。他們和其他的字符串沒有區別,也不會受到特殊對待。只有他們被用作模式串用于函數的時候,'%' 才作為轉義字符。所以,如果你需要在一個模式串內放置引號的話,你必須使用在其他的字符串中放置引號的方法來處理,使用 '/' 轉義引號,'/' 是Lua的轉義符。
你可以使用方括號將字符類或者字符括起來創建自己的字符類(譯者:Lua稱之為char-set,就是指傳統正則表達式概念中的括號表達式)。比如,'[%w_]' 將匹配字母數字和下劃線,'[01]' 匹配二進制數字,'[%[%]]' 匹配一對方括號。
下面的例子統計文本中元音字母出現的次數:
_, nvow = string.gsub(text, "[AEIOUaeiou]", "")
在char-set中可以使用范圍表示字符的集合,第一個字符和最后一個字符之間用連字符連接表示這兩個字符之間范圍內的字符集合。大部分的常用字符范圍都已經預定義好了,所以一般你不需要自己定義字符的集合。比如,'%d' 表示 '[0-9]';'%x' 表示 '[0-9a-fA-F]'。然而,如果你想查找八進制數,你可能更喜歡使用 '[0-7]' 而不是 '[01234567]'。你可以在字符集(char-set)的開始處使用 '^' 表示其補集:'[^0-7]' 匹配任何不是八進制數字的字符;'[^/n]' 匹配任何非換行符戶的字符。記住,可以使用大寫的字符類表示其補集:'%S' 比 '[^%s]' 要簡短些。
可以使用修飾符來修飾模式增強模式的表達能力,Lua中的模式修飾符有四個:
+ 匹配前一字符1次或多次
* 匹配前一字符0次或多次
- 匹配前一字符0次或多次
? 匹配前一字符0次或1次
'+',匹配一個或多個字符,總是進行最長的匹配。比如,模式串 '%a+' 匹配一個或多個字母或者一個單詞:
print(string.gsub("one, and two; and three", "%a+", "word"))
--> word, word word; word word
'%d+' 匹配一個或多個數字(整數):
i, j = string.find("the number 1298 is even", "%d+")
print(i,j) --> 12 15
'*' 與 '+' 類似,但是他匹配一個字符0次或多次出現.一個典型的應用是匹配空白。
比如,為了匹配一對圓括號()或者括號之間的空白,可以使用 '%(%s*%)'。( '%s*' 用來匹配0個或多個空白。由于圓括號在模式中有特殊的含義,所以我們必須使用 '%' 轉義他。)再看一個例子,'[_%a][_%w]*' 匹配Lua程序中的標示符:字母或者下劃線開頭的字母下劃線數字序列。
'-' 與 '*' 一樣,都匹配一個字符的0次或多次出現,但是他進行的是最短匹配。某些時候這兩個用起來沒有區別,但有些時候結果將截然不同。比如,如果你使用模式 '[_%a][_%w]-' 來查找標示符,你將只能找到第一個字母,因為 '[_%w]-' 永遠匹配空。另一方面,假定你想查找C程序中的注釋,很多人可能使用 '/%*.*%*/'(也就是說 "/*" 后面跟著任意多個字符,然后跟著 "*/" )。然而,由于 '.*' 進行的是最長匹配,這個模式將匹配程序中第一個 "/*" 和最后一個 "*/" 之間所有部分:
test = "int x; /* x */ int y; /* y */"
print(string.gsub(test, "/%*.*%*/", "<COMMENT>"))
--> int x; <COMMENT>
然而模式 '.-' 進行的是最短匹配,她會匹配 "/*" 開始到第一個 "*/" 之前的部分:
test = "int x; /* x */ int y; /* y */"
print(string.gsub(test, "/%*.-%*/", "<COMMENT>"))
--> int x; <COMMENT> int y; <COMMENT>
'?' 匹配一個字符0次或1次。舉個例子,假定我們想在一段文本內查找一個整數,整數可能帶有正負號。模式 '[+-]?%d+' 符合我們的要求,它可以匹配像 "-12"、"23" 和 "+1009" 等數字。'[+-]' 是一個匹配 '+' 或者 '-' 的字符類;接下來的 '?' 意思是匹配前面的字符類0次或者1次。
與其他系統的模式不同的是,Lua中的修飾符不能用字符類;不能將模式分組然后使用修飾符作用這個分組。比如,沒有一個模式可以匹配一個可選的單詞(除非這個單詞只有一個字母)。下面我將看到,通常你可以使用一些高級技術繞開這個限制。
以 '^' 開頭的模式只匹配目標串的開始部分,相似的,以 '$' 結尾的模式只匹配目標串的結尾部分。這不僅可以用來限制你要查找的模式,還可以定位(anchor)模式。比如:
if string.find(s, "^%d") then ...
檢查字符串s是否以數字開頭,而
if string.find(s, "^[+-]?%d+$") then ...
檢查字符串s是否是一個整數。
'%b' 用來匹配對稱的字符。常寫為 '%bxy' ,x和y是任意兩個不同的字符;x作為匹配的開始,y作為匹配的結束。比如,'%b()' 匹配以 '(' 開始,以 ')' 結束的字符串:
print(string.gsub("a (enclosed (in) parentheses) line",
"%b()", ""))
--> a line
常用的這種模式有:'%b()' ,'%b[]','%b%{%}' 和 '%b<>'。你也可以使用任何字符作為分隔符。