第六章:字符串进阶

Lua 的字符串处理能力非常强大,尤其是它的模式匹配(Pattern Matching)机制。虽然它不像 Perl 或 Python 的正则表达式(Regex)那样功能全面,但 Lua 的模式匹配设计得更加轻量、高效,足以应对绝大多数日常需求。

本章将详细讲解 Lua 的模式匹配语法、常用的字符串函数以及如何进行高效的文本处理。

6.1 模式匹配基础

Lua 的模式匹配使用一些特殊字符来描述字符串的结构。

字符类 (Character Classes)

字符类用于匹配一组字符中的任意一个。

  • .: 任意字符

  • %a: 字母

  • %c: 控制字符

  • %d: 数字

  • %l: 小写字母

  • %p: 标点符号

  • %s: 空白字符

  • %u: 大写字母

  • %w: 字母和数字

  • %x: 十六进制数字

  • %z: 内部表示为 0 的字符

注意:大写形式表示补集。例如 %D 表示非数字,%S 表示非空白字符。

自定义字符类

使用 [] 可以自定义字符类。

  • [abc]: 匹配 a, b, 或 c

  • [0-9]: 匹配 0 到 9 的数字 (等同于 %d)

  • [^abc]: 匹配除了 a, b, c 以外的字符

修饰符 (Modifiers)

修饰符用于指定字符重复出现的次数。

  • *: 重复 0 次或多次(最长匹配,贪婪)

  • +: 重复 1 次或多次(最长匹配,贪婪)

  • -: 重复 0 次或多次(最短匹配,非贪婪)

  • ?: 重复 0 次或 1 次

锚点 (Anchors)

  • ^: 匹配字符串开头

  • $: 匹配字符串结尾

捕获 (Captures)

使用圆括号 () 可以将匹配到的部分捕获起来,用于后续使用。

6.2 常用字符串函数

string.find (查找)

查找模式在字符串中首次出现的位置。返回起始索引和结束索引。

s = "Hello Lua User"
i, j = string.find(s, "Lua")
print(i, j)        -- 7  9
print(string.sub(s, i, j)) -- Lua

string.match (匹配)

返回匹配到的子串。如果使用了捕获,则返回所有捕获的值。

date = "Today is 15/02/2026"
d, m, y = string.match(date, "(%d+)/(%d+)/(%d+)")
print(d, m, y) -- 15  02  2026

string.gsub (全局替换)

gsub (Global SUBstitution) 用于将所有匹配到的模式替换为指定字符串。

s = "Lua is cute"
new_s, count = string.gsub(s, "cute", "great")
print(new_s)   -- Lua is great
print(count)   -- 1 (替换次数)

-- 使用捕获进行重排
s = "Key = Value"
print(string.gsub(s, "(%w+)%s*=%s*(%w+)", "%2 = %1"))
-- Value = Key

gsub 的第三个参数不仅可以是字符串,还可以是表或函数:

  • : 用匹配到的内容作为键去查表,用查到的值替换。

  • 函数: 用匹配到的内容作为参数调用函数,用返回值替换。

t = {name="lua", version="5.4"}
s = "$name-$version.tar.gz"
print(string.gsub(s, "$(%w+)", t)) 
-- lua-5.4.tar.gz

string.gmatch (迭代匹配)

返回一个迭代器,每次调用返回下一个匹配到的子串。通常配合 for 循环使用。

s = "hello world from lua"
for w in string.gmatch(s, "%a+") do
    print(w)
end
-- 输出:
-- hello
-- world
-- from
-- lua

6.3 进阶技巧

贪婪与非贪婪匹配

默认情况下,*+ 是贪婪的,它们会尽可能多地匹配字符。而 - 是非贪婪的,它会尽可能少地匹配。

s = 'a "one" and "two"'
print(string.match(s, '".*"')) -- "one" and "two" (贪婪)
print(string.match(s, '".-"')) -- "one" (非贪婪)

模式转义

如果要匹配特殊字符(如 ., (, ), %, +, -, *, ?, [, ], ^, $, \0),需要在前面加 % 进行转义。
例如匹配百分号 %,需要写成 %%

边界匹配

%bxy 用于匹配以字符 x 开始并以字符 y 结束的平衡字符串。这对于匹配括号非常有用。

s = "a (enclosed (in) parentheses) line"
print(string.match(s, "%b()")) -- (enclosed (in) parentheses)

练习题

  1. 编写一个函数 trim(s),去除字符串首尾的空白字符。

  2. 给定一个 URL 字符串(如 "http://www.lua.org/manual/5.4/manual.html"),使用模式匹配提取协议(http)、域名(www.lua.org)和路径(/manual/5.4/manual.html)。

  3. 将字符串 "the quick brown fox" 中的每个单词首字母大写。

  4. 解析 CSV 格式的行:"Apple, Banana, \"Grape, Green\", Orange",注意处理引号内的逗号。


下一章预告:我们将学习 Lua 的 I/O 库,掌握如何读取文件、写入文件以及处理标准输入输出流。