Lua


7. 字符串 (String)


文档摘要

字符串 (String) Lua 字符串 (String) 详解与实践指南 引言 1. 字符串基础 1.1 字符串的定义 在 Lua 中,字符串是不可变的字节序列。这意味着一旦字符串被创建,就无法直接修改其内容。任何看似修改字符串的操作实际上都会创建一个新的字符串。 Lua 字符串可以使用以下三种方式定义: 单引号 ( ) 或双引号 ( ): 这是最常用的字符串定义方式,适用于大多数情况。 单引号和双引号在 Lua 中基本等价,唯一的区别在于转义字符的处理。 长括号 ( ): 用于定义多行字符串,可以包含换行符和引号,无需进行额外的转义。 长括号还可以嵌套,例如 , 等,通过等号的数量来区分嵌套的层次。这在处理包含 或 的字符串时非常有用。

7. 字符串 (String)

Lua 字符串 (String) 详解与实践指南

引言

1. 字符串基础

1.1 字符串的定义

在 Lua 中,字符串是不可变的字节序列。这意味着一旦字符串被创建,就无法直接修改其内容。任何看似修改字符串的操作实际上都会创建一个新的字符串。

Lua 字符串可以使用以下三种方式定义:

  • 单引号 (') 或双引号 ("): 这是最常用的字符串定义方式,适用于大多数情况。

    str1 = 'Hello, Lua!' str2 = "World of Strings"

    单引号和双引号在 Lua 中基本等价,唯一的区别在于转义字符的处理。

  • 长括号 ([[ ]]): 用于定义多行字符串,可以包含换行符和引号,无需进行额外的转义。

    str3 = [[ This is a multi-line string in Lua. ]]

    长括号还可以嵌套,例如 [=[ ... ]=], [==[ ... ]==] 等,通过等号的数量来区分嵌套的层次。这在处理包含 [[]] 的字符串时非常有用。

  • 转义序列: Lua 支持常见的转义序列,用于表示特殊字符。

    转义序列 含义
    \a 响铃 (bell)
    \b 退格 (backspace)
    \f 换页 (form feed)
    \n 换行 (newline)
    \r 回车 (carriage return)
    \t 水平制表符 (horizontal tab)
    \v 垂直制表符 (vertical tab)
    \\ 反斜杠 (backslash)
    \" 双引号 (double quote)
    \' 单引号 (single quote)
    \[ 左方括号 (left square bracket)
    \] 右方括号 (right square bracket)

    例如:

    str4 = "This string contains a newline: \n and a tab: \t" str5 = 'This string contains a single quote: \'' str6 = "This string contains a double quote: \""

1.2 字符串的类型

可以使用 type() 函数来检查变量的类型,字符串的类型为 "string"。

print(type(str1)) --> string print(type(str2)) --> string print(type(str3)) --> string

2. 字符串操作

Lua 提供了丰富的内置函数和操作符来处理字符串。

2.1 字符串连接

  • 连接操作符 ..: 用于连接两个或多个字符串。

    str7 = "Hello" .. ", " .. "Lua" print(str7) --> Hello, Lua str8 = "Number: " .. 10 -- Lua 会自动将数字转换为字符串 print(str8) --> Number: 10

    .. 操作符会将非字符串类型的值自动转换为字符串。

  • string.format() 函数: 更灵活的字符串格式化方法,类似于 C 语言的 sprintf 函数。

    str9 = string.format("Name: %s, Age: %d", "Alice", 30) print(str9) --> Name: Alice, Age: 30 str10 = string.format("Pi: %.2f", math.pi) -- 格式化浮点数,保留两位小数 print(str10) --> Pi: 3.14

    string.format() 使用格式化字符串,以 % 开头,后跟格式化指示符 (如 %s, %d, %f 等)。常用的格式化指示符包括:

    指示符 含义
    %s 字符串
    %d 有符号十进制整数
    %i 有符号十进制整数
    %u 无符号十进制整数
    %o 无符号八进制整数
    %x 无符号十六进制整数 (小写字母)
    %X 无符号十六进制整数 (大写字母)
    %f 浮点数 (默认精度)
    %e 科学计数法 (小写 'e')
    %E 科学计数法 (大写 'E')
    %g 使用 %e%f 中较短的形式
    %G 使用 %E%f 中较短的形式
    %c ASCII 码对应的字符
    %% 输出百分号 %

    还可以在格式化指示符中添加修饰符,例如:

    • 宽度: %10s 表示字符串宽度为 10,不足则用空格填充。

    • 精度: %.2f 表示浮点数保留两位小数。

    • 标志:

      • -: 左对齐 (默认右对齐)。 例如 %-10s 左对齐字符串。

      • +: 在正数前显示 + 号。 例如 %+d

      • 0: 用 0 填充空白。 例如 %05d,数字不足 5 位用 0 填充。

2.2 字符串长度

  • # 操作符: 获取字符串的长度 (字节数)。

    str11 = "Lua String" print(#str11) --> 10 str12 = "中文字符串" -- UTF-8 编码,每个汉字通常占 3 个字节 print(#str12) --> 12 (4个汉字 * 3字节/汉字)

    # 操作符返回的是字符串的字节长度,对于 UTF-8 编码的字符串,一个 Unicode 字符可能占用多个字节。

  • string.len() 函数: 与 # 操作符功能相同,获取字符串的字节长度。

    print(string.len(str11)) --> 10 print(string.len(str12)) --> 12

2.3 字符串索引和子串

Lua 字符串是基于 1 的索引,即第一个字符的索引为 1,第二个字符的索引为 2,以此类推。

  • string.sub() 函数: 提取字符串的子串。

    str13 = "Hello, Lua World" sub_str1 = string.sub(str13, 1, 5) -- 从索引 1 到 5 的子串 (包含索引 1 和 5) print(sub_str1) --> Hello sub_str2 = string.sub(str13, 8) -- 从索引 8 到字符串末尾的子串 print(sub_str2) --> Lua World sub_str3 = string.sub(str13, -5) -- 从倒数第 5 个字符到末尾的子串 print(sub_str3) --> World sub_str4 = string.sub(str13, 1, -1) -- 整个字符串 (相当于复制) print(sub_str4) --> Hello, Lua World

    string.sub(s, i, j) 函数返回字符串 s 从索引 ij 的子串。

    • ij 可以是正数或负数。

    • 正数索引从字符串开头计数,负数索引从字符串末尾倒数。

    • 如果省略 j,则默认提取到字符串末尾。

    • 如果 i 大于 j,则返回空字符串。

2.4 字符串查找

  • string.find() 函数: 在字符串中查找指定的模式 (pattern),并返回匹配的起始和结束索引。如果没有找到匹配项,则返回 nil

    str14 = "Lua is a powerful scripting language." start_index, end_index = string.find(str14, "Lua") if start_index then print(string.format("Found 'Lua' at index %d to %d", start_index, end_index)) --> Found 'Lua' at index 1 to 3 else print("'Lua' not found") end start_index2, end_index2 = string.find(str14, "Python") if start_index2 then print("'Python' found") else print("'Python' not found") --> 'Python' not found end start_index3, end_index3 = string.find(str14, "language", 10) -- 从索引 10 开始查找 if start_index3 then print(string.format("Found 'language' starting from index 10 at %d to %d", start_index3, end_index3)) --> Found 'language' starting from index 10 at 28 to 35 end

    string.find(s, pattern, init, plain) 函数:

    • s: 要搜索的字符串。

    • pattern: 要查找的模式字符串。Lua 的模式匹配功能非常强大,稍后会详细介绍。

    • init (可选): 搜索的起始索引,默认为 1。

    • plain (可选): 布尔值,如果为 true,则禁用模式匹配,将 pattern 视为普通字符串进行精确查找。默认为 false (启用模式匹配)。

  • string.match() 函数: 在字符串中查找指定的模式 (pattern),并返回第一个匹配到的子串。如果没有找到匹配项,则返回 nil

    str15 = "Name: John, Age: 25, City: New York" name = string.match(str15, "Name: (%w+)") -- 捕获名字 (一个或多个字母数字字符) print(name) --> John age = string.match(str15, "Age: (%d+)") -- 捕获年龄 (一个或多个数字) print(age) --> 25 city = string.match(str15, "City: (.+)") -- 捕获城市 (任意字符,直到行尾) print(city) --> New York

    string.match(s, pattern, init) 函数:

    • s: 要搜索的字符串。

    • pattern: 要查找的模式字符串,可以使用捕获组 () 来提取匹配的部分。

    • init (可选): 搜索的起始索引,默认为 1。

  • string.gmatch() 函数: 返回一个迭代器,用于遍历字符串中所有匹配指定模式的子串。

    str16 = "apple, banana, orange, grape" for fruit in string.gmatch(str16, "%w+") do -- 匹配一个或多个字母数字字符 print(fruit) end --> apple --> banana --> orange --> grape str17 = "set name='John', age=30, city='London'" for key, value in string.gmatch(str17, "(%w+)=['\"]?([^'\"]*)['\"]?") do -- 捕获键值对 print(string.format("Key: %s, Value: %s", key, value)) end --> Key: name, Value: John --> Key: age, Value: 30 --> Key: city, Value: London

    string.gmatch(s, pattern) 函数返回一个迭代器函数。每次调用迭代器函数,都会返回下一个匹配的子串。可以使用 for ... in 循环来遍历所有匹配项。

2.5 字符串替换

  • string.gsub() 函数: 全局替换字符串中所有匹配指定模式的子串。

    str18 = "replace all spaces with underscores" new_str1 = string.gsub(str18, " ", "_") -- 将所有空格替换为下划线 print(new_str1) --> replace_all_spaces_with_underscores str19 = "count vowels in this string" count, num_replacements = string.gsub(str19, "[aeiouAEIOU]", "") -- 替换所有元音字母为空字符串,并返回替换次数 print(count) --> cnt vwl in ths strng print(num_replacements) --> 8 str20 = "swap words: first second" swapped_str = string.gsub(str20, "(%w+) (%w+)", "%2 %1") -- 交换两个单词的位置,使用捕获组的反向引用 print(swapped_str) --> swap words: second first

    string.gsub(s, pattern, replacement, n) 函数:

    • s: 要进行替换操作的字符串。

    • pattern: 要查找的模式字符串。

    • replacement: 用于替换匹配项的字符串。可以是字符串或函数。

    • n (可选): 最大替换次数。如果指定,则只替换前 n 个匹配项。

    • 函数返回两个值:

      • 替换后的新字符串。

      • 替换的次数。

    replacement 可以是:

    • 字符串: 直接替换匹配项。可以使用 %n (n 为 1-9) 来引用模式中的捕获组。%0 代表整个匹配项。

    • 函数: 每次匹配到模式时,都会调用该函数,并将捕获组作为参数传递给函数。函数的返回值将作为替换字符串。

2.6 字符串分割

虽然 Lua 标准库中没有直接提供字符串分割函数,但可以使用 string.gmatch() 结合模式匹配来实现字符串分割功能。

function split(str, delimiter) local result = {} for segment in string.gmatch(str, "([^" .. delimiter .. "]*)") do -- 匹配非分隔符的字符序列 table.insert(result, segment) end return result end str21 = "apple,banana,orange,grape" fruits = split(str21, ",") for i, fruit in ipairs(fruits) do print(string.format("Fruit %d: %s", i, fruit)) end --> Fruit 1: apple --> Fruit 2: banana --> Fruit 3: orange --> Fruit 4: grape str22 = " leading and trailing spaces " segments = split(str22, " ") -- 使用空格分割,但会包含空字符串 for i, segment in ipairs(segments) do print(string.format("Segment %d: '%s'", i, segment)) end --> Segment 1: '' --> Segment 2: '' --> Segment 3: Segment 4: leading --> Segment 5: and --> Segment 6: trailing --> Segment 7: spaces --> Segment 8: '' --> Segment 9: '' -- 改进的 split 函数,去除空字符串 function split_improved(str, delimiter) local result = {} for segment in string.gmatch(str, "([^" .. delimiter .. "]*)") do if segment ~= "" then -- 忽略空字符串 table.insert(result, segment) end end return result end segments2 = split_improved(str22, " ") for i, segment in ipairs(segments2) do print(string.format("Segment %d: '%s'", i, segment)) end --> Segment 1: 'leading' --> Segment 2: 'and' --> Segment 3: 'trailing' --> Segment 4: 'spaces'

2.7 字符串大小写转换

  • string.lower() 函数: 将字符串转换为小写。

    str23 = "MixedCase String" lower_str = string.lower(str23) print(lower_str) --> mixedcase string
  • string.upper() 函数: 将字符串转换为大写。

    upper_str = string.upper(str23) print(upper_str) --> MIXEDCASE STRING

2.8 字符串反转

function reverse_string(str) local reversed_str = "" for i = #str, 1, -1 do -- 从字符串末尾向前遍历 reversed_str = reversed_str .. string.sub(str, i, i) end return reversed_str end str24 = "hello" reversed = reverse_string(str24) print(reversed) --> olleh

2.9 其他字符串函数

Lua 的 string 库还提供了其他一些有用的函数,例如:

  • string.char(...): 将 ASCII 码转换为字符。

    char_str = string.char(65, 66, 67) -- ASCII 码 65, 66, 67 分别对应 'A', 'B', 'C' print(char_str) --> ABC
  • string.byte(s, i, j): 返回字符串 s 中索引 ij 的字符的 ASCII 码。

    byte_values = string.byte("ABC", 1, 3) for i, byte_value in ipairs{byte_values} do print(string.format("Byte %d: %d", i, byte_value)) end --> Byte 1: 65 --> Byte 2: 66 --> Byte 3: 67
  • string.rep(s, n): 重复字符串 s n 次。

    repeated_str = string.rep("repeat ", 3) print(repeated_str) --> repeat repeat repeat
  • string.reverse(s): 反转字符串 s (与上面自定义的 reverse_string 函数功能相同,但效率更高,是 C 实现的)。

    reversed_str2 = string.reverse("hello") print(reversed_str2) --> olleh
  • string.dump(function): 返回一个包含 Lua 函数二进制表示的字符串。可以将这个字符串保存到文件或通过网络传输,然后在其他地方使用 loadstring()load() 函数加载并执行。

3. Lua 模式匹配

Lua 的模式匹配系统是一种轻量级的正则表达式实现,功能强大且易于学习。它在 string.find(), string.match(), string.gsub(), string.gmatch() 等函数中被广泛使用。

3.1 模式字符

字符或字符类 含义
. 匹配任何字符。
%a 匹配任何字母。
%c 匹配任何控制字符。
%d 匹配任何数字。
%g 匹配除空格外的任何可打印字符。
%l 匹配任何小写字母。
%p 匹配任何标点符号。
%s 匹配任何空白字符 (空格、制表符、换行符等)。
%u 匹配任何大写字母。
%w 匹配任何字母数字字符 (字母或数字)。
%x 匹配任何十六进制数字。
%z 匹配以 \0 字符表示的字符。
%x (x 为非字母数字字符) 代表字符 x 本身。用于转义特殊字符 (例如 `%., %[, %%, 等)。
[set] 匹配集合 set 中的任何字符。可以使用 - 表示范围,例如 [0-9] 表示数字,[a-z] 表示小写字母。
[^set] 匹配不在集合 set 中的任何字符。

3.2 模式修饰符

修饰符 含义
* 匹配 0 个或多个前面的模式项。尽可能多地匹配。 (贪婪匹配)
+ 匹配 1 个或多个前面的模式项。尽可能多地匹配。 (贪婪匹配)
- 匹配 0 个或多个前面的模式项。尽可能少地匹配。 (非贪婪匹配)
? 匹配 0 个或 1 个前面的模式项。

3.3 锚点

锚点 含义
^ 匹配字符串的开头。
$ 匹配字符串的结尾。

3.4 捕获组

使用括号 () 可以创建捕获组,用于提取匹配到的子串。捕获组在 string.match()string.gsub() 函数中非常有用。

3.5 模式匹配示例

str25 = "The price is $19.99 and quantity is 10." -- 查找价格 price = string.match(str25, "%$%d+%.%d+") -- 匹配 '$' 符号,后跟一个或多个数字,一个点号,再后跟一个或多个数字 print(price) --> $19.99 -- 查找所有数字 for num in string.gmatch(str25, "%d+") do -- 匹配一个或多个数字 print(num) end --> 19 --> 99 --> 10 -- 查找单词 for word in string.gmatch(str25, "%w+") do -- 匹配一个或多个字母数字字符 print(word) end --> The --> price --> is --> 19 --> 99 --> and --> quantity --> is --> 10 -- 替换所有数字为 '*' replaced_str2 = string.gsub(str25, "%d+", "*") print(replaced_str2) --> The price is $*.** and quantity is **. -- 使用捕获组交换两个单词 str26 = "word1 word2" swapped_str2 = string.gsub(str26, "(%w+) (%w+)", "%2 %1") -- 捕获两个单词,并使用反向引用交换位置 print(swapped_str2) --> word2 word1 -- 使用非贪婪匹配 str27 = "<a>tag1</a> <a>tag2</a>" tags = {} for tag in string.gmatch(str27, "<a>(.+)</a>") do -- 贪婪匹配会匹配到 "tag1</a> <a>tag2" table.insert(tags, tag) end print(table.concat(tags, ", ")) --> tag1</a> <a>tag2 (错误的结果) tags_non_greedy = {} for tag in string.gmatch(str27, "<a>(.-)</a>") do -- 非贪婪匹配,使用 '-' 修饰符 table.insert(tags_non_greedy, tag) end print(table.concat(tags_non_greedy, ", ")) --> tag1, tag2 (正确的结果)

4. 字符串编码和 Unicode

Lua 字符串本质上是字节序列,它本身并不关心字符编码。这意味着 Lua 可以处理任何编码的字符串,包括 ASCII, UTF-8, Latin-1 等。


发布者: 作者: 转发
评论区 (0)
U