模式匹配函數(shù)
在string庫(kù)中功能最強(qiáng)大的函數(shù)是:
string.find (字符串查找)
string.gsub (全局字符串替換)
string.gfind (全局字符串查找)
這些函數(shù)都是基于模式匹配的昧港。
與其他腳本語(yǔ)言不同的是,Lua并不使用POSIX規(guī)范的正則表達(dá)式(也寫作regexp)來(lái)進(jìn)行模式匹配(譯者:POSIX是unix的工業(yè)標(biāo)準(zhǔn),regexp最初來(lái)源于unix,POSIX對(duì)regexp也作了規(guī)范)裸违。主要的原因出于程序大小方面的考慮:實(shí)現(xiàn)一個(gè)典型的符合POSIX標(biāo)準(zhǔn)的regexp大概需要4000行代碼厌秒,這比整個(gè)Lua標(biāo)準(zhǔn)庫(kù)加在一起都大村缸。權(quán)衡之下,Lua中的模式匹配的實(shí)現(xiàn)只用了500行代碼痢虹,當(dāng)然這意味著不可能實(shí)現(xiàn)POSIX所規(guī)范的所有更能被去。然而,Lua中的模式匹配功能是很強(qiáng)大的奖唯,并且包含了一些使用標(biāo)準(zhǔn)POSIX模式匹配不容易實(shí)現(xiàn)的功能惨缆。
string.find
s = "hello world"
i, j = string.find(s, "hello")
print(i, j) --> 1 5
print(string.sub(s, i, j)) --> hello
print(string.find(s, "world")) --> 7 11
i, j = string.find(s, "l")
print(i, j) --> 3 3
print(string.find(s, "lll")) --> nil
string.find的基本應(yīng)用就是用來(lái)在 s 內(nèi)搜索匹配指定的模式(第二個(gè)參數(shù))的串。函數(shù)如果找到匹配的串返回他的位置丰捷,否則返回nil.例如最簡(jiǎn)單的模式就是一個(gè)單詞坯墨,僅僅匹配單詞本身。比如病往,模式'hello'僅僅匹配目標(biāo)串中的"hello"畅蹂。當(dāng)查找到模式的時(shí)候,函數(shù)返回兩個(gè)值:匹配串開始索引和結(jié)束索引荣恐。
例子中液斜,匹配成功的時(shí)候累贤,string.sub利用string.find返回的值截取匹配的子串。 (對(duì)簡(jiǎn)單模式而言少漆,匹配的就是其本身臼膏。)
string.find函數(shù)第三個(gè)參數(shù)是可選的:標(biāo)示目標(biāo)串中搜索的起始位置。當(dāng)我們想查找目標(biāo)串中所有匹配的子串的時(shí)候示损,這個(gè)選項(xiàng)非常有用渗磅。我們可以不斷的循環(huán)搜索,每一次從前一次匹配的結(jié)束位置開始检访。下面看一個(gè)例子始鱼,下面的代碼用一個(gè)字符串中所有的新行構(gòu)造一個(gè)表:
local t = {} -- table to store the indices
local i = 0
while true do
i = string.find(s, "/n", i+1) -- find 'next' newline
if i == nil then break end
t.insert(t, i)
end
后面我們還會(huì)看到可以使用string.gfind迭代子來(lái)簡(jiǎn)化上面這個(gè)循環(huán)。
string.gsub
string.gsub函數(shù)有三個(gè)參數(shù):目標(biāo)串脆贵,模式串医清,替換串。他基本作用是用來(lái)查找匹配模式的串卖氨,并將使用替換串其替換掉:
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
第四個(gè)參數(shù)是可選的会烙,用來(lái)限制替換的范圍:
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的第二個(gè)返回值表示他進(jìn)行替換操作的次數(shù)。例如筒捺,下面代碼用來(lái)計(jì)算一個(gè)字符串中空格出現(xiàn)的次數(shù):
_, count = string.gsub(str, " ", " ")
注意, _ 只是一個(gè)啞元變量.
模式
你還可以在模式串中使用字符類柏腻。字符類指可以匹配一個(gè)特定字符集合內(nèi)任何字符的模式項(xiàng)。
比如系吭,字符類 %d 匹配任意數(shù)字. 所以你可以使用模式串'%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 控制字符
%d 數(shù)字
%l 小寫字母
%p 標(biāo)點(diǎn)字符
%s 空白符
%u 大寫字母
%w 字母和數(shù)字
%x 十六進(jìn)制數(shù)字
%z 代表0的字符
上面字符類的大寫形式表示小寫所代表的集合的補(bǔ)集五嫂。例如, '%A'非字母的字符:
print(string.gsub("hello, up-down!", "%A", "."))
--> hello..up.down. 4
數(shù)字4不是字符串結(jié)果的一部分,他是gsub返回的第二個(gè)結(jié)果肯尺,代表發(fā)生替換的次數(shù)沃缘。下面其他的關(guān)于打印gsub結(jié)果的例子中將會(huì)忽略這個(gè)數(shù)值。
在模式匹配中有一些特殊字符蟆盹,他們有特殊的意義孩灯,Lua中的特殊字符如下:
( ) . % + - * ? [ ^ $
'%'用作特殊字符的轉(zhuǎn)義字符,因此 '%.' 匹配點(diǎn); '%%'匹配字符 '%′ .轉(zhuǎn)義字符'%'不僅可以用來(lái)轉(zhuǎn)義特殊字符逾滥,還可以用于所有的非字母的字符峰档。當(dāng)對(duì)一個(gè)字符有疑問的時(shí)候,為安全起見請(qǐng)使用轉(zhuǎn)義字符轉(zhuǎn)義他寨昙。
對(duì)Lua而言讥巡,模式串就是普通的字符串。他們和其他的字符串沒有區(qū)別舔哪,也不會(huì)受到特殊對(duì)待欢顷。只有他們被用作模式串用于函數(shù)的時(shí)候,'%'才作為轉(zhuǎn)義字符捉蚤。所以抬驴,如果你需要在一個(gè)模式串內(nèi)放置引號(hào)的話炼七,你必須使用在其他的字符串中放置引號(hào)的方法來(lái)處理,使用'/'轉(zhuǎn)義引號(hào)布持,'/'是Lua的轉(zhuǎn)義符豌拙。你可以使用方括號(hào)將字符類或者字符括起來(lái)創(chuàng)建自己的字符類(譯者:Lua稱之為char-set,就是指?jìng)鹘y(tǒng)正則表達(dá)式概念中的括號(hào)表達(dá)式)题暖。比如按傅,'[%w_]'將匹配字母數(shù)字和下劃線,'[01]'匹配二進(jìn)制數(shù)字胧卤,'[%[%]]'匹配一對(duì)方括號(hào)唯绍。下面的例子統(tǒng)計(jì)文本中元音字母出現(xiàn)的次數(shù):
_, nvow = string.gsub(text, "[AEIOUaeiou]", "")
在char-set中可以使用范圍表示字符的集合,第一個(gè)字符和最后一個(gè)字符之間用連字符連接表示這兩個(gè)字符之間范圍內(nèi)的字符集合枝誊。大部分的常用字符范圍都已經(jīng)預(yù)定義好了况芒,所以一般你不需要自己定義字符的集合。比如侧啼,'%d'表示 '[0-9]'牛柒;'%x'表示'[0-9a-fA-F]'堪簿。然而痊乾,如果你想查找八進(jìn)制數(shù),你可能更喜歡使用'[0-7]'而不是'[01234567]'椭更。你可以在字符集(char-set)的開始處使用 '^' 表示其補(bǔ)集: '[^0-7]' 匹配任何不是八進(jìn)制數(shù)字的字符哪审; '[^/n]' 匹配任何非換行符戶的字符。記住虑瀑,可以使用大寫的字符類表示其補(bǔ)集: '%S'比'[^%s]'要簡(jiǎn)短些.
Lua的字符類依賴于本地環(huán)境湿滓,所以'[a-z]'可能與'%l'表示的字符集不同。在一般情況下舌狗,后者包括?′ 和
?′叽奥,而前者沒有。應(yīng)該盡可能的使用后者來(lái)表示字母痛侍,除非出于某些特殊考慮朝氓,因?yàn)楹笳吒?jiǎn)單、方便主届、更高效赵哲。
可以使用修飾符來(lái)修飾模式增強(qiáng)模式的表達(dá)能力,Lua中的模式修飾符有四個(gè):
+ 匹配前一字符1次或多次
* 匹配前一字符0次或多次
- 匹配前一字符0次或多次
? 匹配前一字符0次或1次
'+' 匹配一個(gè)或多個(gè)字符君丁,她總是進(jìn)行最長(zhǎng)的匹配. 比如枫夺,模式串 '%a+'匹配一個(gè)或多個(gè)字母或者一個(gè)單詞 :
print(string.gsub("one, and two; and three", "%a+", "word"))
--> word, word word; word word
'%d+'匹配一個(gè)或多個(gè)數(shù)字 (整數(shù)):
i, j = string.find("the number 1298 is even", "%d+")
print(i,j) --> 12 15
'*' 與 '+'類似, 但是他匹配一個(gè)字符0次或多次出現(xiàn).一個(gè)典型的應(yīng)用是匹配空白。比如绘闷,為了匹配一對(duì)圓括號(hào)()或者( )之間的空白橡庞,可以使用'%(%s%)'. ( '%s'用來(lái)匹配0個(gè)或多個(gè)空白. 由于圓括號(hào)在模式中有特殊的含義较坛,所以我們必須使用'%′轉(zhuǎn)義他.)
再看一個(gè)例子,'[%a][%w]'匹配Lua程序中的標(biāo)示符:字母或者下劃線開頭的字母下劃線數(shù)字序列扒最。
'-'與'*′一樣燎潮,都匹配一個(gè)字符的0次或多次出現(xiàn),但是他進(jìn)行的是最短匹配扼倘。某些時(shí)候這兩個(gè)用起來(lái)沒有區(qū)別确封,但有些時(shí)候結(jié)果將截然不同。比如再菊,如果你使用模式'[%a][%w]-'來(lái)查找標(biāo)示符,你將只能找到第一個(gè)字母秉剑,因?yàn)?[_%w]-'永遠(yuǎn)匹配空侦鹏。另一方面略水,假定你想查找C程序中的注釋劝萤,很多人可能使用 '/%.%/' (也就是說(shuō) "/" 后面跟著任意多個(gè)字符床嫌,然后跟著 "/"). 然而厌处,由于 '.'進(jìn)行的是最長(zhǎng)匹配阔涉,這個(gè)模式將匹配程序中第一個(gè)"/" 和最后一個(gè)"*/"之間所有部分:
test = "int x; /* x */ int y; /* y */"
print(string.gsub(test, "/%*.*%*/", "<COMMENT>"))
--> int x; <COMMENT>
然而模式 '.-'進(jìn)行的是最短匹配洒敏,她會(huì)匹配"/*"開始到第一個(gè)"*/"之前的部分:
test = "int x; /* x */ int y; /* y */"
print(string.gsub(test, "/%*.-%*/", "<COMMENT>"))
--> int x; <COMMENT> int y; <COMMENT>
'?'匹配一個(gè)字符0次或1次.舉個(gè)例子凶伙,假定我們想在一段文本內(nèi)查找一個(gè)整數(shù),整數(shù)可能帶有正負(fù)號(hào)显押。 模式 '[+-]?%d+'符合我們的要求,她可以匹配 像 "-12", "23" 和 "+1009"等數(shù)字. '[+-]' 是一個(gè)匹配'+′或者 '-′的字符類挖息;接下來(lái)的 '?'意思是匹配前面的字符類0次或者1次.
與其他系統(tǒng)的模式不同的是兽肤,Lua中的修飾符不能用字符類;不能將模式分組然后使用修飾符作用這個(gè)分組电禀。比如尖飞,沒有一個(gè)模式可以匹配一個(gè)可選的單詞(除非這個(gè)單詞只有一個(gè)字母)。下面我將看到沮明,通常你可以使用一些高級(jí)技術(shù)繞開這個(gè)限制珊擂。
以'^′開頭的模式只匹配目標(biāo)串的開始部分,相似的挚歧,以`$′結(jié)尾的模式只匹配目標(biāo)串的結(jié)尾部分吁峻。這不僅可以用來(lái)限制你要查找的模式用含,還可以定位(anchor)模式矮慕。比如:
if string.find(s, "^%d") then ...
檢查字符串s是否以數(shù)字開頭,而
if string.find(s, "^[+-]?%d+$") then ...
檢查字符串s是否是一個(gè)整數(shù)痴鳄。
'%b'用來(lái)匹配對(duì)稱的字符.常寫為 '%bxy',x和y是任意兩個(gè)不同的字符缸夹;x作為匹配的開始,y作為匹配的結(jié)束痪寻。比如, '%b()'匹配以'(′開始橡类, 以 ')′結(jié)束的字符串:
print(string.gsub("a (enclosed (in) parentheses) line",
"%b()", ""))
--> a line
常用的這種模式有: '%b()', '%b[]', '%b%{%}',和 '%b<>'蛇尚。你也可以使用任何字符作為分隔符。
參考文章:
lua 中的正則表達(dá)式