第一版是為 Lua 5.0 編寫的。雖然在很大程度上仍然適用於後續版本,但有一些不同之處。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您也可以幫助 支援 Lua 專案。
![]() |
用 Lua 程式設計 | ![]() |
第三部分。標準函式庫 第 20 章。字串函式庫 |
擷取機制允許模式擷取與模式部分相符的主題字串部分,以供進一步使用。透過在括號中撰寫您想要擷取的模式部分,來指定擷取。
當您對 string.find
指定擷取時,它會將擷取的值作為呼叫的額外結果傳回。此功能的典型用法是將字串分解成數個部分
pair = "name = Anna" _, _, key, value = string.find(pair, "(%a+)%s*=%s*(%a+)") print(key, value) --> name Anna模式 '
%a+
' 指定非空白字母序列;模式 '%s*
' 指定可能為空白的空白序列。因此,在上述範例中,整個模式指定一個字母序列,後接一個空白序列,後接 `=
´,再後接空白加上另一個字母序列。兩個字母序列的模式都用括號括起來,以便在發生相符時將它們擷取。find
函式總是先傳回相符發生的索引(我們在先前的範例中將其儲存在虛擬變數 _
中),然後傳回模式相符期間所做的擷取。以下是類似的範例
date = "17/7/1990" _, _, d, m, y = string.find(date, "(%d+)/(%d+)/(%d+)") print(d, m, y) --> 17 7 1990
我們也可以在樣式中使用擷取。在樣式中,一個像「%d
」的項目,其中 d 是單一數字,只會比對 d-th 擷取的副本。作為一個典型用法,假設你想要在字串中尋找,一個被單引號或雙引號包住的子字串。你可以嘗試一個像「["'].-["']
」的樣式,也就是一個引號接著任何東西,接著另一個引號;但你會遇到像 "it's all right"
這樣的字串問題。為了解決這個問題,你可以擷取第一個引號並用它來指定第二個引號
s = [[then he said: "it's all right"!]] a, b, c, quotedPart = string.find(s, "([\"'])(.-)%1") print(quotedPart) --> it's all right print(c) --> "第一個擷取是引號字元本身,第二個擷取是引號的內容(比對「
.-
」的子字串)。
擷取值的第三個用法是在 gsub
的替換字串中。就像樣式一樣,替換字串可能包含像「%d
」的項目,在進行替換時,它們會變更為各自的擷取。(順帶一提,因為這些變更,替換字串中的「%
´」必須轉譯為「"%%"
」。)舉例來說,下列指令會複製字串中的每個字母,並在副本之間加上連字號
print(string.gsub("hello Lua!", "(%a)", "%1-%1")) --> h-he-el-ll-lo-o L-Lu-ua-a!這個會交換相鄰字元
print(string.gsub("hello Lua", "(.)(.)", "%2%1")) --> ehll ouLa
作為一個更有用的範例,讓我們寫一個原始格式轉換器,它會取得一個字串,其中包含以 LaTeX 樣式編寫的指令,例如
\command{some text}並將它們變更為 XML 樣式的格式,
<command>some text</command>對於這個規格,下列程式碼會完成工作
s = string.gsub(s, "\\(%a+){(.-)}", "<%1>%2</%1>")例如,如果
s
是字串
the \quote{task} is to \em{change} that.那個
gsub
呼叫會將它變更為
the <quote>task</quote> is to <em>change</em> that.另一個有用的範例是如何修剪字串
function trim (s) return (string.gsub(s, "^%s*(.-)%s*$", "%1")) end注意樣式格式的明智使用。兩個錨點(「
^
」和「$
」)確保我們取得整個字串。因為「.-
」會嘗試盡可能少地擴充,所以兩個樣式「%s*
」會比對兩端的所有空格。另外請注意,因為 gsub
會傳回兩個值,我們使用額外的括號來捨棄額外的結果(計數)。
擷取值的最後一個用法可能是最有力的。我們可以呼叫 string.gsub
,並將函式作為其第三個引數,而不是替換字串。當以這種方式呼叫時,string.gsub
會在每次找到比對時呼叫指定的函式;此函式的引數是擷取,而函式傳回的值會用作替換字串。作為第一個範例,下列函式會執行「變數擴充」:它會將全域變數 varname
的值替換為字串中每個出現的 $varname
function expand (s) s = string.gsub(s, "$(%w+)", function (n) return _G[n] end) return s end name = "Lua"; status = "great" print(expand("$name is $status, isn't it?")) --> Lua is great, isn't it?如果你不確定指定的變數是否有字串值,你可以將
tostring
套用至它們的值
function expand (s) return (string.gsub(s, "$(%w+)", function (n) return tostring(_G[n]) end)) end print(expand("print = $print; a = $a")) --> print = function: 0x8050ce0; a = nil
一個更有力的範例使用 loadstring
來評估我們寫在方括號中,並在前面加上美元符號的文字中的完整表達式
s = "sin(3) = $[math.sin(3)]; 2^5 = $[2^5]" print((string.gsub(s, "$(%b[])", function (x) x = "return " .. string.sub(x, 2, -2) local f = loadstring(x) return f() end))) --> sin(3) = 0.1411200080598672; 2^5 = 32第一個匹配是字串
"$[math.sin(3)]"
,其對應的擷取是 "[math.sin(3)]"
。呼叫 string.sub
會從擷取的字串中移除括號,因此載入執行用的字串會是 "return math.sin(3)"
。匹配 "$[2^5]"
也是一樣。
我們經常需要一種 string.gsub
,只會在字串上反覆運算,而不關心結果字串。例如,我們可以使用以下程式碼將字串中的字詞收集到一個表格中
words = {} string.gsub(s, "(%a+)", function (w) table.insert(words, w) end)如果
s
是字串 "hello hi, again!"
,在執行該指令後,word
表格會是
{"hello", "hi", "again"}
string.gfind
函數提供更簡單的方式來撰寫該程式碼
words = {} for w in string.gfind(s, "(%a)") do table.insert(words, w) end
gfind
函數非常適合搭配一般 for 迴圈使用。它會傳回一個函數,在字串中反覆運算模式的所有出現位置。
我們可以再簡化一點該程式碼。當我們呼叫 gfind
時,如果沒有明確擷取模式,該函數會擷取整個模式。因此,我們可以將前一個範例改寫成這樣
words = {} for w in string.gfind(s, "%a") do table.insert(words, w) end
在我們的下一個範例中,我們使用 URL 編碼,這是 HTTP 用來在 URL 中傳送參數的編碼。此編碼會將特殊字元(例如 `=
´、`&
´ 和 `+
´)編碼成 "%XX"
,其中 XX 是該字元的十六進位表示法。然後,它會將空格變更為 `+
´。例如,它會將字串 "a+b = c"
編碼成 "a%2Bb+%3D+c"
。最後,它會在每個參數名稱和參數值之間寫入 `=
´,並在所有 name=value
對之間加上連字號。例如,值
name = "al"; query = "a+b = c"; q="yes or no"會編碼成
name=al&query=a%2Bb+%3D+c&q=yes+or+no現在,假設我們要解碼此 URL,並將每個值儲存在一個表格中,其索引為對應的名稱。以下函數會執行基本解碼
function unescape (s) s = string.gsub(s, "+", " ") s = string.gsub(s, "%%(%x%x)", function (h) return string.char(tonumber(h, 16)) end) return s end第一個陳述式會將字串中的每個 `
+
´ 變更為空格。第二個 gsub
會匹配所有在 `%
´ 之後的兩位數十六進位數字,並呼叫一個匿名函數。該函數會將十六進位數字轉換為數字(tonumber
,底數為 16),並傳回對應的字元(string.char
)。例如,
print(unescape("a%2Bb+%3D+c")) --> a+b = c
若要解碼 name=value
對,我們會使用 gfind
。由於名稱和值都不能包含 `&
´ 或 `=
´,我們可以使用模式 '[^&=]+
' 來匹配它們
cgi = {} function decode (s) for name, value in string.gfind(s, "([^&=]+)=([^&=]+)") do name = unescape(name) value = unescape(value) cgi[name] = value end end對
gfind
的呼叫會比對所有符合 name=value
格式的配對,而對於每個配對,迭代器會傳回對應的擷取 (如比對字串中的括號所標示) 作為 name
和 value
的值。迴圈主體會對兩個字串呼叫 unescape
,並將配對儲存於 cgi
表格中。
對應的編碼也很容易撰寫。首先,我們撰寫 escape
函式;此函式會將所有特殊字元編碼為 `%
´ 後接十六進制的字元 ASCII 碼 (format
選項 "%02X"
會建立一個兩位數的十六進制數字,並使用 0 作為補齊),然後將空白變更為 `+
´
function escape (s) s = string.gsub(s, "([&=+%c])", function (c) return string.format("%%%02X", string.byte(c)) end) s = string.gsub(s, " ", "+") return s end
encode
函式會遍歷要編碼的表格,並建立結果字串
function encode (t) local s = "" for k,v in pairs(t) do s = s .. "&" .. escape(k) .. "=" .. escape(v) end return string.sub(s, 2) -- remove first `&' end t = {name = "al", query = "a+b = c", q="yes or no"} print(encode(t)) --> q=yes+or+no&query=a%2Bb+%3D+c&name=al
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |