範例教學 |
|
在讀完本教學後,強烈建議閱讀有關範例的手冊[1],這樣你才能知道它提供的所有功能。
首先,我們將使用 string.find
函式,它會找出字串中範例的第一個出現位置,並傳回比對文字的第一個和最後一個字元的開始和結束索引
> = string.find('banana', 'an') -- find 1st occurance of 'an' (letters are matched literally) 2 3 > = string.find('banana', 'lua') -- 'lua' will not be found nil
但是實際比對文字並沒有那麼有用,因此範例有 字元類別 的概念。字元類別是一種範例,用於比對一組字元中的其中一個。例如,.
是比對任何字元的字元類別
> = string.find("abcdefg", 'b..') 2 4
我們現在可以使用這些索引取得比對的文字,但有一個更好的方法:string.match
函式。它會傳回比對的文字,如果沒找到範例則傳回 nil:(實際上,find 也會傳回比對的文字,但它會先傳回索引;match 只傳回文字)
> = string.match("abcdefg", 'b..') bcd
範例有幾個預先定義的類別,使用 "%x" 的方式使用它們,其中 "x" 是識別類別的字母
> = string.match("foo 123 bar", '%d%d%d') -- %d matches a digit 123 > = string.match("text with an Uppercase letter", '%u') -- %u matches an uppercase letter U
你也可以建立你自己的類別,方法是用方括弧包覆一組字元。這將會比對其中一個字元。如果括弧內的第一個字元是 ^
,則它會比對 不在 群組中的字元。
> = string.match("abcd", '[bc][bc]') bc > = string.match("abcd", '[^ad]') b > = string.match("123", '[0-9]') -- you can specify a range of characters using - 1
即使使用字元類別,這仍然有很大的限制,因為我們只能比對長度固定的字串。為了解決這個問題,範例支援以下四個重複運算子
*
比對前一個字元 (或類別) 零次或多次,盡可能多次。+
比對前一個字元 (或類別) 一次或多次,盡可能多次。-
比對前一個字元 (或類別) 零次或多次,盡可能少次。?
讓前一個字元 (或類別) 成為選擇性的。我們從 ?
開始,因為它是其中最簡單的
> = string.match("examples", 'examples?') examples > = string.match("example", 'examples?') example > = string.match("example", 'examples') nil
現在來看 +
的範例。請注意它是如何與一個類別結合使用的,這樣它就可以比對一個序列的不同字元
> = string.match("this is some text with a number 12345 in it", '%d+') 12345
與 +
不同,*
可以比對空白
> = string.match("one |two| three", '|.*|') |two| > = string.match("one || three", '|.*|') || > = string.match("one || three", '|.+|') nil
使用 +
和 *
時常犯的一個錯誤是沒有意識到它們會盡可能地比對,而這可能不是你想要的結果。你可以使用 -
來修正這個問題
> = string.match("one |two| three |four| five", '|.*|') |two| three |four| > = string.match("one |two| three |four| five", '|.-|') |two| > = string.match("one |two| three |four| five", '|[^|]*|') -- another solution can be to not let the contents match the delimiter |two|
在使用 -
時,你必須記得從兩側「錨定」它,否則它不會比對出任何結果 (因為它會嘗試盡可能少比對)
> = string.match("abc", 'a.*') abc > = string.match("abc", 'a.-') -- the .- part matches nothing a > = string.match("abc", 'a.-$') -- the $ matches the end of the string abc > = string.match("abc", '^.-b') -- the ^ matches the start of the string ab
^
和 $
,它們分別匹配字串的開頭和結尾。它們不只可用於 -
,你可以用 ^
作為模式的前置詞,以讓它匹配開頭,用 $
作為後置詞,以讓它匹配結尾,並用兩個包覆(就像上面的範例)以讓它匹配整個字串。最後,你可能會思考如何逐字匹配所有這些特殊字元。解決辦法是在它們前面加上 % 字元
> = string.match("%*^", '%%%*%^') %*^
如果你想從字串中擷取特定片段時,你可以將這些模式的片段包在 ( )
中,而每段擷取的內容會從 string.match
傳回。
> = string.match("foo: 123 bar: 456", '(%a+):%s*(%d+)%s+(%a+):%s*(%d+)') -- %a: letter %s: whitespace foo 123 bar 456
每個擷取都會以單一結果傳回,這有助於分割出值
date = "04/19/64" m, d, y = string.match(date, "(%d+)/(%d+)/(%d+)") print("19" .. y) --> 1964
有關擷取的詳細資訊請見手冊 [2]
特別如果你習慣於其他有正則表示法的語言,你可能會預期可以像這樣執行
'(foo)+' -- match the string "foo" repeated one or more times '(foo|bar)' -- match either the string "foo" or the string "bar"
可惜 Lua 的模式不支援這個,只有單一字元可以被重複或選擇,子模式或字串不行。解決方案是使用多個模式並撰寫一些自訂邏輯,使用正則表示法函式庫例如 lrexlib 或 Lua PCRE,或使用 LPeg [3]。LPeg 是強大的基於解析式文法 [4] 的 Lua 文字解析函式庫。它提供在 Lua 程式碼中建立和組合模式的功能,還提供一個有點類似 Lua 模式或正則表示法,以便於建立小型的解析器。
再說一次,現在你對模式的工作原理已經有概念,請見模式手冊[1],以找出所有可能性。