1. 程式人生 > 其它 >LUA字串模式匹配

LUA字串模式匹配

轉載:https://www.cnblogs.com/whiteyun/archive/2009/09/02/1541043.html

模式匹配函式

在string庫中功能最強大的函式是:

string.find(字串查詢)
string.gsub(全域性字串替換)
string.gfind(全域性字串查詢)
string.gmatch(返回查詢到字串的迭代器)

這些函式都是基於模式匹配的。
與其他指令碼語言不同的是,Lua並不使用POSIX規範的正則表示式[4](也寫作regexp)來進行模式匹配。主要的原因出於程式大小方面的考慮:實現一個典型的符合POSIX標準的regexp大概需要4000行程式碼,這比整個Lua標準庫加在一起都大。權衡之下,Lua中的模式匹配的實現只用了500行程式碼,當然這意味著不可能實現POSIX所規範的所有更能。然而,Lua中的模式匹配功能是很強大的,並且包含了一些使用標準POSIX模式匹配不容易實現的功能。

string.gmatch(str, pattern)      

這是一個返回迭代器的函式. 實際的用例如下:

s = "hello world from Lua"
for w in string.gmatch(s, "%a+") do
 print(w)
end

這裡是一個捕獲並將配對字元分別存到不同變數的例子:

t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
 t[k]=v
end
for k, v in pairs(t) do
 print(k, v)
end

string.gsub(str, pattern, repl, n)

string.gsub()函式根據給定的配對錶達式對源字串str進行配對, 同時返回源字串的一個副本, 該副本中成功配對的所有子字串都將被替換. 函式還將返回成功配對的次數.實際的替換行為由repl引數的型別決定:

當repl為字串時, 所有成功配對的子字串均會被替換成指定的repl字串.
當repl為table時, 對每個成功配對的子字串, 函式均會試圖尋找以其為key值的table中的元素, 並返回該元素. 如果該配對包含任何捕獲資訊, 則以編號為1號的捕獲作為key值進行查詢.
當repl為函式時, 每個成功配對的子字串均會作為引數被傳入到該函式中去.
在repl是table或函式時, 如果該table或函式返回了字串或數字的值, 這個值依然會被用於替換副本字串中的配對子字串. 如果該table/函式返回的值為空, 將不發生替換.

n引數可選, 當它被指定時, string.gsub()函式只對源字串中的前n個成功配對的成員進行操作.

以下是幾個例子:

> print(string.gsub("hello world", "(%w+)", "%1 %1"))
hello hello world world 2

> print(string.gsub("hello Lua", "(%w+)%s*(%w+)", "%2 %1"))
Lua hello 1

> string.gsub("hello world", "%w+", print)
hello world 2

> lookupTable = {["hello"] = "hola", ["world"] = "mundo"}
> print(string.gsub("hello world", "(%w+)", lookupTable))
hola mundo 2


string.match(str, pattern, init)

string.match()只尋找源字串str中的第一個配對. 引數init可選, 指定搜尋過程的起點, 預設為1.

在成功配對時, 函式將返回配對錶達式中的所有捕獲結果; 如果沒有設定捕獲標記, 則返回整個配對字串. 當沒有成功的配對時, 返回nil.


string.match("abcdaef", "a")
-> a

string.reverse(str)
返回一個字串的倒序排列
string.reverse("abcde")
->edcba

string.dump(function)
返回指定函式的二進位制程式碼(函式必須是一個Lua函式,並且沒有上值)


string.find(str, pattern, init, plain)
string.find的基本應用就是用來在目標串(subject string)內搜尋匹配指定的模式的串。函式如果找到匹配的串返回他的位置,否則返回nil.最簡單的模式就是一個單詞,僅僅匹配單詞本身。比如,模式'hello'僅僅匹配目標串中的"hello"。當查詢到模式的時候,函式返回兩個值:匹配串開始索引和結束索引。
s = "hello world"
string.find(s, "hello")    --> 1    5
string.find(s, "world")    --> 7    11
string.find(s, "l")        --> 3    3
string.find(s, "lll")      --> nil
string.find函式第三個引數是可選的:標示目標串中搜索的起始位置。當我們想查詢目標串中所有匹配的子串的時候,這個選項非常有用。我們可以不斷的迴圈搜尋,每一次從前一次匹配的結束位置開始。下面看一個例子,下面的程式碼用一個字串中所有的新行構造一個表:
local t = {}      -- 存放回車符的位置
local i = 0
while true do
    i = string.find(s, "\n", i+1)  -- 查詢下一行
    if i == nil then break end
    table.insert(t, i)
end

string.sub(str,sPos,ePos)
string.gsub的功能是擷取字串,他從指定起始位置擷取一個字串。string.sub可以利用string.find返回的值擷取匹配的子串。
對簡單模式而言,匹配的就是其本身
s = "hello world"
local i, j = string.find(s, "hello")    --> 1    5
string.sub(s, i, j)        --> hello

string.gsub(str, sourcestr, desstr)
string.gsub的基本作用是用來查詢匹配模式的串,並將使用替換串其替換掉:
string.gsub函式有三個引數:目標串,模式串,替換串。
s = string.gsub("Lua is cute", "cute", "great")
print(s)      --> Lua is great
s = string.gsub("all lii", "l", "x")
print(s)      --> axx xii
s = string.gsub("Lua is great", "perl", "tcl")
print(s)      --> Lua is great
第四個引數是可選的,用來限制替換的範圍:
s = string.gsub("all lii", "l", "x", 1)
print(s)          --> axl lii
s = string.gsub("all lii", "l", "x", 2)
print(s)          --> axx lii
string.gsub的第二個返回值表示他進行替換操作的次數。例如,下面程式碼湧來計算一個字串中空格出現的次數:
_, count = string.gsub(str, " ", " ")
(注意,_ 只是一個啞元變數)


模式

你還可以在模式串中使用字元類。字元類指可以匹配一個特定字元集合內任何字元的模式項。比如,字元類%d匹配任意數字。所以你可以使用模式串'%d%d/%d%d/%d%d%d%d'搜尋dd/mm/yyyy格式的日期:
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支援的所有字元類:

單個字元(除^$()%.[]*+-?外): 與該字元自身配對
.(點): 與任何字元配對
%a: 與任何字母配對
%c: 與任何控制符配對(例如\n)
%d: 與任何數字配對
%l: 與任何小寫字母配對
%p: 與任何標點(punctuation)配對
%s: 與空白字元配對
%u: 與任何大寫字母配對
%w: 與任何字母/數字配對
%x: 與任何十六進位制數配對
%z: 與任何代表0的字元配對
%x(此處x是非字母非數字字元): 與字元x配對. 主要用來處理表達式中有功能的字元(^$()%.[]*+-?)的配對問題, 例如%%與%配對
[數個字元類]: 與任何[]中包含的字元類配對. 例如[%w_]與任何字母/數字, 或下劃線符號(_)配對
[^數個字元類]: 與任何包含在[]中的字元類配對. 例如[^%s]與任何非空白字元配對

當上述的字元類用大寫書寫時, 表示與非此字元類的任何字元配對. 例如, %S表示與任何非空白字元配對.例如,'%A'非字母的字元
print(string.gsub("hello, up-down!", "%A", "."))
    --> hello..up.down. 4
(數字4不是字串結果的一部分,他是gsub返回的第二個結果,代表發生替換的次數。下面其他的關於列印gsub結果的例子中將會忽略這個數值。)在模式匹配中有一些特殊字元,他們有特殊的意義,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的字元類依賴於本地環境,所以 '[a-z]' 可能與 '%l' 表示的字符集不同。在一般情況下,後者包括 'ç' 和 'ã',而前者沒有。應該儘可能的使用後者來表示字母,除非出於某些特殊考慮,因為後者更簡單、方便、更高效。
可以使用修飾符來修飾模式增強模式的表達能力,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<>'。你也可以使用任何字元作為分隔符。