範例教學

lua-users home
wiki

Lua 範例可以比對字元序列,其中每個字元可以是選擇性的,或重複多次。如果你習慣使用其他具備正規表示式以比對文字的語言,請記住 Lua 的範例比對並不相同:它受到更多限制,且具有不同的語法。

在讀完本教學後,強烈建議閱讀有關範例的手冊[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
將 % 後面的字母改成大寫會反轉類別,因此 %D 將會比對所有非數字字元。請參閱教程頂端連結的範例手冊,以取得所有預先定義類別的清單。

你也可以建立你自己的類別,方法是用方括弧包覆一組字元。這將會比對其中一個字元。如果括弧內的第一個字元是 ^,則它會比對 不在 群組中的字元。

> = 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]

Lua 模式的限制

特別如果你習慣於其他有正則表示法的語言,你可能會預期可以像這樣執行

'(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],以找出所有可能性。


RecentChanges · 喜好設定
編輯 · 歷程記錄
最後編輯時間為 2019 年 4 月 12 日上午 6:03 GMT (差異)