13.2 字符串库 (String Library) Lua 字符串库 (String Library) 详解与实践指南 1. 引言:Lua 字符串库的重要性 在 Lua 中,字符串是一种基本的数据类型,用于表示文本信息。Lua 的字符串是不可变的,这意味着任何字符串操作都不会直接修改原始字符串,而是返回一个新的字符串。 这种设计保证了数据的一致性和可预测性。 标准库中的字符串库, 库,为 Lua 开发者提供了丰富的工具箱,用于处理各种字符串相关的任务。无论是简单的字符串拼接、查找子串,还是复杂的文本格式化、模式匹配, 库都能提供高效且便捷的解决方案。 熟练掌握 库,是编写高效、健壮 Lua 代码的关键一步。 2.
1. 引言:Lua 字符串库的重要性
在 Lua 中,字符串是一种基本的数据类型,用于表示文本信息。Lua 的字符串是不可变的,这意味着任何字符串操作都不会直接修改原始字符串,而是返回一个新的字符串。 这种设计保证了数据的一致性和可预测性。
标准库中的字符串库,string 库,为 Lua 开发者提供了丰富的工具箱,用于处理各种字符串相关的任务。无论是简单的字符串拼接、查找子串,还是复杂的文本格式化、模式匹配,string 库都能提供高效且便捷的解决方案。 熟练掌握 string 库,是编写高效、健壮 Lua 代码的关键一步。
2. 字符串库概览:核心功能一览
Lua 的字符串库 string 提供了一系列函数,主要可以分为以下几个类别:
基本字符串操作: 字符串长度、子串提取、字符串拼接、大小写转换等。
字符串查找与匹配: 查找子串位置、模式匹配、替换子串等。
字符处理: 字符编码转换、字符码值获取等。
格式化输出: 根据格式化字符串生成新的字符串。
其他实用功能: 字符串反转、重复字符串等。
接下来,我们将逐一深入探讨这些功能,并结合代码示例进行详细说明。
3. 基本字符串操作:构建字符串处理的基础
3.1. string.len(s):获取字符串长度
string.len(s) 函数返回字符串 s 的长度(字节数)。在 Lua 中,字符串可以包含任意字节,包括 ASCII 字符和 UTF-8 编码的多字节字符。
local str = "Hello, Lua!" local length = string.len(str) print("字符串长度:", length) -- 输出: 字符串长度: 11 local utf8_str = "你好,世界!" local utf8_length = string.len(utf8_str) print("UTF-8 字符串长度 (字节):", utf8_length) -- 输出: UTF-8 字符串长度 (字节): 16 -- 注意:UTF-8 编码中,一个汉字通常占用 3 个字节,标点符号也可能占用多个字节。
3.2. string.sub(s, i, j):提取子字符串
string.sub(s, i, j) 函数从字符串 s 中提取子字符串,起始位置为 i,结束位置为 j(包含)。索引从 1 开始,并且可以为负数,负数索引表示从字符串末尾开始计数,-1 表示最后一个字符,-2 表示倒数第二个字符,以此类推。
i:起始索引 (包含)。
j:结束索引 (包含,可选)。 如果省略 j,则默认提取到字符串末尾。
local str = "Programming in Lua" local sub1 = string.sub(str, 1, 11) -- 提取 "Programming" print("子字符串 1:", sub1) -- 输出: 子字符串 1: Programming local sub2 = string.sub(str, 13) -- 提取 "Lua" (从第 13 个字符到末尾) print("子字符串 2:", sub2) -- 输出: 子字符串 2: Lua local sub3 = string.sub(str, -3) -- 提取最后 3 个字符 "Lua" print("子字符串 3:", sub3) -- 输出: 子字符串 3: Lua local sub4 = string.sub(str, 1, -4) -- 提取 "Programming in L" (从第 1 个字符到倒数第 4 个字符) print("子字符串 4:", sub4) -- 输出: 子字符串 4: Programming in L
3.3. string.lower(s) 和 string.upper(s):大小写转换
string.lower(s) 函数将字符串 s 中的所有大写字母转换为小写字母,并返回新的字符串。
string.upper(s) 函数则相反,将所有小写字母转换为大写字母。
local str = "Mixed Case String" local lower_str = string.lower(str) print("小写字符串:", lower_str) -- 输出: 小写字符串: mixed case string local upper_str = string.upper(str) print("大写字符串:", upper_str) -- 输出: 大写字符串: MIXED CASE STRING
3.4. string.rep(s, n):重复字符串
string.rep(s, n) 函数将字符串 s 重复 n 次,并返回新的字符串。
local str = "abc" local repeated_str = string.rep(str, 3) print("重复字符串:", repeated_str) -- 输出: 重复字符串: abcabcabc
3.5. string.reverse(s):反转字符串
string.reverse(s) 函数将字符串 s 反转,并返回新的字符串。
local str = "olleh" local reversed_str = string.reverse(str) print("反转字符串:", reversed_str) -- 输出: 反转字符串: hello
4. 字符串查找与匹配:在文本中寻觅信息
4.1. string.find(s, pattern, init, plain):查找子字符串
string.find(s, pattern, init, plain) 函数在字符串 s 中查找第一个匹配 pattern 的子字符串。
s:要搜索的字符串。
pattern:要查找的模式字符串。 可以是简单的子字符串,也可以是 Lua 模式。
init:可选参数,指定搜索的起始位置(索引,默认为 1)。
plain:可选参数,布尔值。如果为 true,则禁用模式匹配,将 pattern 视为普通字符串进行精确查找 (默认为 false)。
函数返回两个值:
起始索引: 如果找到匹配项,则返回匹配子字符串的起始索引。
结束索引: 如果找到匹配项,则返回匹配子字符串的结束索引。
nil: 如果未找到匹配项,则返回 nil。
local str = "Hello, World! Lua is awesome." -- 查找普通子字符串 local start_index, end_index = string.find(str, "Lua") if start_index then print("找到 'Lua',起始位置:", start_index, ", 结束位置:", end_index) -- 输出: 找到 'Lua',起始位置: 14 , 结束位置: 16 else print("未找到 'Lua'") end -- 从指定位置开始查找 local start_index2, end_index2 = string.find(str, "o", 5) -- 从第 5 个字符开始查找 'o' if start_index2 then print("找到 'o' (从位置 5 开始),起始位置:", start_index2, ", 结束位置:", end_index2) -- 输出: 找到 'o' (从位置 5 开始),起始位置: 8 , 结束位置: 8 else print("未找到 'o' (从位置 5 开始)") end -- 精确查找 (禁用模式匹配) local start_index3, end_index3 = string.find(str, ".", 1, true) -- 查找字面量 '.' if start_index3 then print("找到 '.' (精确查找),起始位置:", start_index3, ", 结束位置:", end_index3) -- 输出: 找到 '.' (精确查找),起始位置: 13 , 结束位置: 13 else print("未找到 '.' (精确查找)") end -- 使用模式匹配 (将在 4.3 节详细介绍) local start_index4, end_index4 = string.find(str, "%w+") -- 查找第一个单词 (%w+ 表示一个或多个字母数字字符) if start_index4 then print("找到第一个单词,起始位置:", start_index4, ", 结束位置:", end_index4) -- 输出: 找到第一个单词,起始位置: 1 , 结束位置: 5 else print("未找到单词") end
4.2. string.match(s, pattern, init):提取匹配的子字符串
string.match(s, pattern, init) 函数在字符串 s 中查找第一个匹配 pattern 的子字符串,并返回 捕获 的部分。 如果模式中没有捕获组,则返回整个匹配的子字符串。
s:要搜索的字符串。
pattern:要匹配的模式字符串。
init:可选参数,指定搜索的起始位置(索引,默认为 1)。
local str = "Name: John Doe, Age: 30" -- 提取姓名 (使用捕获组 '()') local name = string.match(str, "Name: (%w+ %w+)") -- 捕获两个单词作为姓名 if name then print("姓名:", name) -- 输出: 姓名: John Doe else print("未找到姓名") end -- 提取年龄 (使用捕获组 '%d+') local age = string.match(str, "Age: (%d+)") -- 捕获一个或多个数字作为年龄 if age then print("年龄:", age) -- 输出: 年龄: 30 else print("未找到年龄") end -- 提取多个捕获组 (返回多个值) local name2, age2 = string.match(str, "Name: (%w+ %w+), Age: (%d+)") if name2 and age2 then print("姓名:", name2, ", 年龄:", age2) -- 输出: 姓名: John Doe , 年龄: 30 else print("未找到姓名或年龄") end -- 没有捕获组,返回整个匹配 local match_str = string.match(str, "Age: %d+") if match_str then print("匹配字符串:", match_str) -- 输出: 匹配字符串: Age: 30 else print("未匹配") end
4.3. Lua 模式匹配 (Pattern Matching):强大的文本查找工具
Lua 的模式匹配是一种轻量级的模式语言,用于在字符串中查找和操作文本。它类似于正则表达式,但语法更简洁,效率更高。 string.find 和 string.match 等函数都支持模式匹配。
模式字符类:
. (点): 匹配任何字符。
%a: 匹配任何字母。
%c: 匹配任何控制字符。
%d: 匹配任何数字。
%g: 匹配任何除空格外的可打印字符。
%l: 匹配任何小写字母。
%p: 匹配任何标点符号。
%s: 匹配任何空白字符(空格、制表符、换行符等)。
%u: 匹配任何大写字母。
%w: 匹配任何字母数字字符。
%x: 匹配任何十六进制数字。
%z: 匹配字符 '\0'。
%x (x 为非字母数字字符): 表示字符 x 本身(用于转义特殊字符,例如 `%., %+, %[, %( 等)。
模式修饰符:
+: 匹配前一个字符类的一个或多个重复项。
*: 匹配前一个字符类的零个或多个重复项。
-: 匹配前一个字符类的零个或多个重复项 (最短匹配)。
?: 匹配前一个字符类的零个或一个。
^: 在模式的开头使用,表示匹配字符串的开头。
$: 在模式的结尾使用,表示匹配字符串的结尾。
(...):捕获组,用于提取匹配的子字符串。
字符集:
[...]: 自定义字符集。例如 [aeiou] 匹配任何元音字母。 [^aeiou] 匹配任何非元音字母。 可以使用范围,例如 [0-9] 匹配数字,[a-z] 匹配小写字母。示例:
local str = "Email: user@example.com, Phone: 123-456-7890" -- 匹配邮箱地址 (简单模式) local email = string.match(str, "%w+@%w+%.%w+") -- 较简单的邮箱模式,可能不够严谨 if email then print("邮箱:", email) -- 输出: 邮箱: user@example.com else print("未找到邮箱") end -- 匹配更复杂的邮箱地址模式 (更严谨的模式) local email2 = string.match(str, "([%w_.-]+)@([%w_.-]+)%.([%w_.-]+)") -- 使用捕获组 if email2 then print("邮箱 (更严谨模式):", email2) -- 输出: 邮箱 (更严谨模式): user@example.com else print("未找到邮箱 (更严谨模式)") end -- 匹配电话号码 (简单模式) local phone = string.match(str, "%d+-%d+-%d+") if phone then print("电话号码:", phone) -- 输出: 电话号码: 123-456-7890 else print("未找到电话号码") end -- 匹配以 "Email" 开头的行 local email_line = string.match("Email: test@test.com\nOther Line", "^Email:.*") -- '^' 匹配行首, '.*' 匹配任意字符零次或多次 if email_line then print("邮箱行:", email_line) -- 输出: 邮箱行: Email: test@test.com else print("未找到邮箱行") end
4.4. string.gmatch(s, pattern):迭代匹配器
string.gmatch(s, pattern) 函数返回一个迭代器,用于遍历字符串 s 中所有匹配 pattern 的子字符串。 每次迭代返回下一个匹配项。
local str = "apple, banana, orange, apple" -- 查找所有 "apple" for match in string.gmatch(str, "apple") do print("找到 'apple':", match) end -- 输出: -- 找到 'apple': apple -- 找到 'apple': apple -- 查找所有单词 for word in string.gmatch(str, "%w+") do print("单词:", word) end -- 输出: -- 单词: apple -- 单词: banana -- 单词: orange -- 单词: apple -- 查找所有数字 (如果字符串中有数字) local str_with_numbers = "item1: 10, item2: 20" for num_str in string.gmatch(str_with_numbers, "%d+") do local num = tonumber(num_str) -- 将数字字符串转换为数字 print("数字:", num) end -- 输出: -- 数字: 10 -- 数字: 20
4.5. string.gsub(s, pattern, repl, n):替换子字符串
string.gsub(s, pattern, repl, n) 函数在字符串 s 中查找所有匹配 pattern 的子字符串,并用 repl 替换它们。
s:要操作的字符串。
pattern:要查找的模式字符串。
repl:替换字符串。 可以是字符串,也可以是函数。
n:可选参数,指定最大替换次数(默认为替换所有匹配项)。
函数返回两个值:
替换后的字符串。
替换发生的次数。
local str = "Hello, World! Hello, Lua!" -- 替换所有 "Hello" 为 "Hi" local new_str1, count1 = string.gsub(str, "Hello", "Hi") print("替换后字符串:", new_str1, ", 替换次数:", count1) -- 输出: 替换后字符串: Hi, World! Hi, Lua! , 替换次数: 2 -- 只替换第一个 "Hello" local new_str2, count2 = string.gsub(str, "Hello", "Hi", 1) print("替换后字符串 (只替换一次):", new_str2, ", 替换次数:", count2) -- 输出: 替换后字符串 (只替换一次): Hi, World! Hello, Lua! , 替换次数: 1 -- 使用函数作为替换 (将匹配到的单词转换为大写) local new_str3, count3 = string.gsub(str, "%w+", function(word) return string.upper(word) end) print("替换后字符串 (函数替换):", new_str3, ", 替换次数:", count3) -- 输出: 替换后字符串 (函数替换): HELLO, WORLD! HELLO, LUA! , 替换次数: 4 -- 使用捕获组和替换字符串中的占位符 local str2 = "Swap words: first second" local new_str4, count4 = string.gsub(str2, "(%w+) (%w+)", "%2 %1") -- %1 和 %2 分别代表第一个和第二个捕获组 print("替换后字符串 (捕获组):", new_str4, ", 替换次数:", count4) -- 输出: 替换后字符串 (捕获组): Swap words: second first , 替换次数: 1
5. 字符处理:操作字符编码
5.1. string.byte(s, i, j):获取字符的字节码
string.byte(s, i, j) 函数返回字符串 s 中位置 i 到 j (包含) 的字符的数字字节码值。 如果省略 j,则默认为 i。 如果省略 i,则默认为 1。
local str = "ABC" local byte1 = string.byte(str, 1) -- 获取第一个字符 'A' 的字节码 print("'A' 的字节码:", byte1) -- 输出: 'A' 的字节码: 65 local byte2 = string.byte(str, 1, 3) -- 获取 'A', 'B', 'C' 的字节码 print("'ABC' 的字节码:", byte2) -- 输出: 'ABC' 的字节码: 65 66 67 local byte3 = string.byte(str) -- 获取第一个字符的字节码 (默认 i=1) print("第一个字符的字节码 (默认):", byte3) -- 输出: 第一个字符的字节码 (默认): 65
5.2. string.char(...):将字节码转换为字符
string.char(...) 函数将零个或多个整数参数转换为对应的字符,并连接成一个字符串返回。 参数必须是合法的字节码值。
local char1 = string.char(65, 66, 67) -- 将字节码 65, 66, 67 转换为字符 print("字节码转换为字符:", char1) -- 输出: 字节码转换为字符: ABC local char2 = string.char(97) -- 将字节码 97 转换为字符 'a' print("字节码 97 转换为字符:", char2) -- 输出: 字节码 97 转换为字符: a
6. 格式化输出:灵活构建字符串
6.1. string.format(formatstring, ...):格式化字符串
string.format(formatstring, ...) 函数根据格式化字符串 formatstring 和提供的参数,生成格式化后的字符串。 它类似于 C 语言的 sprintf 函数。
格式化占位符:
%c: 字符 (接收一个整数,并将其转换为字符)。
%d: 有符号十进制整数。
%i: 有符号十进制整数。
%e: 科学计数法 (例如 -1.234e+05)。
%E: 科学计数法 (大写 E,例如 -1.234E+05)。
%f: 浮点数。
%g: 根据数值大小自动选择 %e 或 %f 格式。
%G: 根据数值大小自动选择 %E 或 %f 格式。
%o: 无符号八进制整数。
%q: 可被 Lua 安全读取的字符串(在字符串周围添加双引号,并正确转义内部的双引号和反斜杠)。
%s: 字符串。
%u: 无符号十进制整数。
%x: 无符号十六进制整数 (小写字母)。
%X: 无符号十六进制整数 (大写字母)。
%%: 百分号字符 '%' 本身。
修饰符:
宽度修饰符: 在 '%' 符号后和格式化字符前可以添加一个数字,表示字段的最小宽度。 如果要格式化的值比宽度小,则会用空格填充(默认右对齐)。 例如 %10s 表示字符串字段宽度至少为 10。
精度修饰符: 对于浮点数格式化 (%f, %e, %E, %g, %G),可以在宽度修饰符后添加一个点号 '.' 和一个数字,表示小数点后的位数。 例如 %.2f 表示保留两位小数。
对齐标志: 可以在 '%' 符号后和宽度修饰符前添加对齐标志:
-: 左对齐。 例如 %-10s 表示左对齐,字段宽度至少为 10。
+: 在正数前显示加号。 例如 %+d。
0: 用零填充空白位置 (仅适用于数字格式)。 例如 %05d 表示用零填充到 5 位宽度。
(空格): 在正数前显示空格。 例如 % d。
示例:
local name = "Alice" local age = 30 local score = 95.5 local formatted_str = string.format("Name: %s, Age: %d, Score: %.2f", name, age, score) print("格式化字符串:", formatted_str) -- 输出: 格式化字符串: Name: Alice, Age: 30, Score: 95.50 local hex_num = 255 local hex_str = string.format("Hexadecimal: %X, Decimal: %d", hex_num, hex_num) print("十六进制和十进制:", hex_str) -- 输出: 十六进制和十进制: Hexadecimal: FF, Decimal: 255 local quoted_str = "Lua string with \"quotes\"" local safe_str = string.format("Safe string: %q", quoted_str) print("安全字符串:", safe_str) -- 输出: 安全字符串: Safe string: "Lua string with \"quotes\"" local aligned_str = string.format("Left-aligned: %-10s, Right-aligned: %10s", "left", "right") print("对齐字符串:", aligned_str) -- 输出: 对齐字符串: Left-aligned: left , Right-aligned: right local zero_padded_num = 123 local padded_str = string.format("Zero-padded: %05d", zero_padded_num) print("零填充数字:", padded_str) -- 输出: 零填充数字: Zero-padded: 00123