7.1 字符串的表示


文档摘要

7.1 字符串的表示 Lua 字符串的表示详解 1. 字符串字面量 字符串字面量是直接在 Lua 代码中表示字符串值的方式。Lua 提供了多种字符串字面量形式,以适应不同的场景和需求。 1.1 单引号字符串 ('...') 单引号字符串是最简单的字符串字面量形式。字符串内容被包裹在单引号 之间。 特点: 简单直接: 语法简洁,易于理解。 有限的转义序列: 单引号字符串中只支持两种转义序列: : 表示单引号字符本身。 : 表示反斜杠字符本身。 示例: 限制: 由于单引号字符串只支持两种转义序列,对于包含较多特殊字符(如换行符、制表符等)的字符串,使用单引号字符串可能不够方便。 1.2 双引号字符串 ("...") 双引号字符串是另一种常见的字符串字面量形式。字符串内容被包裹在双引号 之间。

7.1 字符串的表示

Lua 字符串的表示详解

1. 字符串字面量

字符串字面量是直接在 Lua 代码中表示字符串值的方式。Lua 提供了多种字符串字面量形式,以适应不同的场景和需求。

1.1 单引号字符串 ('...')

单引号字符串是最简单的字符串字面量形式。字符串内容被包裹在单引号 ' 之间。

str1 = 'Hello, Lua!' print(str1) -- 输出: Hello, Lua! print(type(str1)) -- 输出: string

特点:

  • 简单直接: 语法简洁,易于理解。

  • 有限的转义序列: 单引号字符串中只支持两种转义序列:

    • \': 表示单引号字符本身。

    • \\: 表示反斜杠字符本身。

示例:

str2 = 'It\'s a sunny day.' -- 使用 \' 转义单引号 print(str2) -- 输出: It's a sunny day. str3 = 'This is a backslash: \\' -- 使用 \\ 转义反斜杠 print(str3) -- 输出: This is a backslash: \ -- 在单引号字符串中使用双引号,不需要转义 str4 = 'He said, "Hello!"' print(str4) -- 输出: He said, "Hello!"

限制:

由于单引号字符串只支持两种转义序列,对于包含较多特殊字符(如换行符、制表符等)的字符串,使用单引号字符串可能不够方便。

1.2 双引号字符串 ("...")

双引号字符串是另一种常见的字符串字面量形式。字符串内容被包裹在双引号 " 之间。

str5 = "Welcome to Lua" print(str5) -- 输出: Welcome to Lua print(type(str5)) -- 输出: string

特点:

  • 更丰富的转义序列: 双引号字符串支持更多的转义序列,可以表示更多特殊字符,包括:

    • \": 表示双引号字符本身。

    • \\: 表示反斜杠字符本身。

    • \n: 换行符 (newline)。

    • \r: 回车符 (carriage return)。

    • \t: 制表符 (tab)。

    • \b: 退格符 (backspace)。

    • \f: 换页符 (form feed)。

    • \v: 垂直制表符 (vertical tab)。

    • \ooo: 使用最多三位八进制数字表示的字符 (例如 \049 表示字符 '1')。

    • \xhh: 使用两位十六进制数字表示的字符 (例如 \x41 表示字符 'A')。

    • \u{h...h}: Unicode 字符编码,花括号内包含一个或多个十六进制数字 (例如 \u{265E} 表示 Unicode 字符 ♞)。

    • \z: 跳过后续的所有空白字符,直到遇到第一个非空白字符。

示例:

str6 = "This is a line\nwith a newline." -- 使用 \n 表示换行符 print(str6) -- 输出: -- This is a line -- with a newline. str7 = "Tab\tseparated\tvalues." -- 使用 \t 表示制表符 print(str7) -- 输出: Tab separated values. str8 = "Double quote: \"" -- 使用 \" 转义双引号 print(str8) -- 输出: Double quote: " str9 = "Character code (octal): \065" -- 使用八进制转义表示字符 '5' print(str9) -- 输出: Character code (octal): 5 str10 = "Character code (hexadecimal): \x41" -- 使用十六进制转义表示字符 'A' print(str10) -- 输出: Character code (hexadecimal): A str11 = "Unicode character: \u{265E}" -- 使用 Unicode 转义表示字符 ♞ print(str11) -- 输出: Unicode character: ♞ str12 = "Skip whitespaces\z and continue." -- 使用 \z 跳过空白字符 print(str12) -- 输出: Skip whitespacesand continue. -- 在双引号字符串中使用单引号,不需要转义 str13 = "He said, 'Goodbye!'" print(str13) -- 输出: He said, 'Goodbye!'

选择单引号还是双引号?

在 Lua 中,单引号字符串和双引号字符串在功能上非常相似。主要区别在于它们支持的转义序列略有不同。选择使用哪种引号通常取决于个人偏好和字符串内容。

  • 如果字符串中包含单引号,且不包含双引号或需要较多转义序列,则使用双引号字符串可能更方便,反之亦然。

  • 如果字符串内容比较简单,且不需要转义特殊字符,则单引号和双引号都可以使用。

  • 保持代码风格一致性也很重要,可以根据项目或团队的约定选择统一使用单引号或双引号。

**1.3 长括号字符串 ([[...]]) 和 ([===[...]====])**

长括号字符串提供了一种定义多行字符串和包含特殊字符的字符串的强大方式。它使用双中括号 [[]] 或者中间包含若干个等号的长括号 [===[]===] 来包裹字符串内容。

str14 = [[ This is a multi-line string using long brackets. ]] print(str14) -- 输出: -- This is a -- multi-line string -- using long brackets. str15 = [===[ This string contains [[nested long brackets]] and [===[even more nested ones]===]. ]===] print(str15) -- 输出: -- This string contains -- [[nested long brackets]] -- and [===[even more nested ones]===].

特点:

  • 多行字符串: 长括号字符串可以跨越多行,字符串中的换行符会被保留。

  • 原始字符串: 长括号字符串内部的字符会被视为原始字符,除了 [[]] 本身以及相同等级的长括号结束符外,不进行任何转义。这意味着你可以直接在长括号字符串中包含单引号、双引号、反斜杠等字符,而无需转义。

  • 嵌套长括号: 可以使用不同数量等号的长括号来实现嵌套。例如,[===[...]====] 可以包含 [[...]] 而不会提前结束字符串。

示例:

str16 = [[ This string contains: - Single quote: ' - Double quote: " - Backslash: \ - Newline: (newline character here) ]] print(str16) -- 输出: -- This string contains: -- - Single quote: ' -- - Double quote: " -- - Backslash: \ -- - Newline: -- (newline character here) str17 = [====[ This string contains a literal [[long bracket]] and a [===[nested long bracket]===]. ]====] print(str17) -- 输出: -- This string contains a literal -- [[long bracket]] -- and a [===[nested long bracket]===].

移除首尾换行符:

如果长括号字符串的第一个字符是换行符,则该换行符会被自动移除。这在编写多行字符串字面量时可以避免不必要的起始空行。

str18 = [[ First line will not be empty. Second line. ]] print(str18) -- 输出: -- First line will not be empty. -- Second line.

选择长括号字符串的场景:

  • 多行文本: 当需要表示多行文本内容时,长括号字符串是最方便的选择。

  • 包含大量特殊字符: 当字符串中包含大量单引号、双引号、反斜杠等特殊字符,且不想进行大量转义时,长括号字符串可以简化代码。

  • 代码块或数据块: 长括号字符串常用于嵌入代码块(例如 SQL 查询语句、HTML 代码)或数据块(例如 JSON 或 XML 数据)到 Lua 代码中。

1.4 Unicode 字符表示 (\u{h...h} 和 \U{h...h...h})

Lua 支持使用 Unicode 字符编码来表示字符串中的字符。这对于处理国际化文本和特殊符号非常重要。

  • \u{h...h}: 使用花括号 {} 包裹 1 到 6 位十六进制数字 h...h 表示 Unicode 代码点,例如 \u{4E00} 表示汉字 "一"。这种形式支持 Unicode 基本多文种平面 (BMP) 内的代码点 (U+0000 到 U+FFFF)。

  • \U{h...h...h}: 使用大写 U 和花括号 {} 包裹 7 或 8 位十六进制数字 h...h...h 表示完整的 Unicode 代码点,可以表示超出 BMP 的辅助平面字符 (U+10000 到 U+10FFFF)。

示例:

str19 = "你好,Lua!\u{4F60}\u{597D}\u{FF0C}Lua\u{FF01}" -- 使用 Unicode 表示 "你好,Lua!" print(str19) -- 输出: 你好,Lua! str20 = "\u{1F600} \u{1F604} \u{1F60A}" -- 表情符号 print(str20) -- 输出: -- 使用 \U 表示辅助平面字符 (需要 Lua 5.4 或更高版本支持) -- str21 = "\U{1D11E}" -- 音乐符号 G 谱号 () -- print(str21) -- 注意:如果 Lua 版本不支持 \U,或者使用了超出 \u 范围的代码点,可能会导致错误或输出不正确的字符。

2. 字符串在内存中的表示

在 Lua 中,字符串被实现为字节序列 (byte sequence)。这意味着 Lua 字符串本质上是一串连续的字节,而不是字符序列。Lua 字符串可以包含任意字节值,包括 NUL 字节 (字符编码为 0 的字节)。

关键特性:

  • 字节序列: Lua 字符串不假设任何特定的字符编码,它仅仅存储字节数据。这意味着 Lua 可以处理各种字符编码的文本,包括 ASCII、UTF-8、Latin-1 等。

  • 不可变性 (Immutability): Lua 字符串是不可变的。一旦创建,字符串的内容就不能被修改。任何字符串操作(例如连接、子串提取)都会返回一个新的字符串对象,而不会改变原始字符串。

  • 字符串内化 (String Interning): Lua 为了优化内存使用和性能,会进行字符串内化。这意味着相同的字符串字面量(在代码中直接写的字符串)在运行时可能会共享同一个内存地址。这可以减少重复字符串的内存占用,并提高字符串比较的效率。但要注意,字符串内化是 Lua 的内部优化机制,不应该依赖于它在所有情况下的行为。

示例:字符串不可变性

str22 = "hello" str23 = str22 .. " world" -- 字符串连接操作创建新字符串 print(str22) -- 输出: hello (str22 仍然是原始字符串) print(str23) -- 输出: hello world (str23 是新创建的字符串)

示例:字符串内化 (可能的结果,取决于 Lua 实现)

str24 = "test" str25 = "test" str26 = string.sub("testing", 1, 4) -- 动态创建的字符串 print(str24 == str25) -- 输出: true (字面量 "test" 可能被内化,共享内存) print(str24 == str26) -- 输出: true (内容相同,但 str26 是动态创建的,可能也被内化) print(str24 === str25) -- 输出: true (字面量通常会共享内存) print(str24 === str26) -- 输出: 可能是 true 或 false (动态创建的字符串是否内化取决于 Lua 实现和运行时环境) -- === 是一个自定义的函数,用于检查两个字符串是否是同一个对象 (内存地址相同) function (a, b) return a == b and tostring(a) == tostring(b) end

3. 字符串操作与字符编码

由于 Lua 字符串是字节序列,在进行字符串操作时,需要注意字符编码的影响,尤其是在处理非 ASCII 字符(例如 UTF-8 编码的中文、日文等)时。

Lua 字符串函数:

Lua 的 string 库提供了一系列用于操作字符串的函数,例如:

  • string.len(s): 返回字符串 s 的长度(字节数)。

  • string.sub(s, i, j): 返回字符串 s 从第 i 个字节到第 j 个字节的子串。

  • string.byte(s, i): 返回字符串 s 的第 i 个字节的数值表示 (ASCII 码值)。

  • string.char(i1, i2, ...): 将一系列数值转换为对应的字符,并连接成字符串。

  • string.upper(s): 将字符串 s 转换为大写。

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

  • string.find(s, pattern, init, plain): 在字符串 s 中查找模式 pattern

  • string.gsub(s, pattern, replacement, n): 在字符串 s 中替换模式 pattern

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

UTF-8 编码处理:

当处理 UTF-8 编码的字符串时,需要注意以下几点:

  • 字节长度 vs. 字符长度: string.len(s) 返回的是字节长度,而不是字符长度。对于 UTF-8 字符串,一个字符可能由 1 到 4 个字节组成。如果需要获取 UTF-8 字符串的字符长度,需要使用专门的 UTF-8 库,例如 utf8 库 (Lua 5.3+ 内置)。

  • 子串提取: 使用 string.sub(s, i, j) 提取子串时,是以字节为单位的。如果截取的位置不恰当,可能会导致截断 UTF-8 字符,产生乱码。同样,对于 UTF-8 字符串的子串操作,建议使用 UTF-8 库提供的函数。

  • 字符迭代: 如果需要遍历 UTF-8 字符串中的字符,不能简单地按字节迭代。需要使用 UTF-8 库提供的字符迭代器。

示例:UTF-8 字符串处理 (需要 Lua 5.3+ 和 utf8 库)

-- UTF-8 字符串示例 (汉字 "你好") utf8_str = "你好" print(string.len(utf8_str)) -- 输出: 6 (字节长度,每个汉字占 3 个字节) -- print(utf8.len(utf8_str)) -- Lua 5.3+ 使用 utf8.len 获取字符长度,输出: 2 -- 字节级别的子串操作可能导致乱码 sub_str_byte = string.sub(utf8_str, 1, 3) -- 截取前 3 个字节 print(sub_str_byte) -- 输出: �� (可能显示乱码,因为截断了 UTF-8 字符) -- 正确的 UTF-8 字符迭代 (Lua 5.3+) -- for char in utf8.graphemes(utf8_str) do -- print(char) -- 输出: 你 和 好 (逐个字符输出) -- end -- 注意: utf8 库在 Lua 5.3+ 中是内置的,但在早期版本中可能需要单独安装。

总结

Lua 提供了灵活多样的字符串字面量形式,包括单引号、双引号和长括号字符串,以及 Unicode 字符表示方法。理解不同字面量形式的特点,选择合适的表示方式可以提高代码的可读性和可维护性。

Lua 字符串在内存中以字节序列的形式存储,具有不可变性和字符串内化等特性。在进行字符串操作时,尤其是在处理非 ASCII 字符时,需要注意字符编码的影响。对于 UTF-8 编码的字符串,建议使用 Lua 5.3+ 内置的 utf8 库或者其他第三方 UTF-8 库来处理,以确保字符操作的正确性。

掌握 Lua 字符串的表示和操作是 Lua 编程的基础,也是编写高效、可靠的 Lua 代码的关键。通过本文的详细讲解和代码示例,相信读者能够更深入地理解 Lua 字符串,并在实际开发中灵活运用。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U