- 6.4 – 字符串处理
- string.byte (s [, i [, j]])
- string.char (···)
- string.dump (function [, strip])
- string.find (s, pattern [, init [, plain]])
- string.format (formatstring, ···)
- string.gmatch (s, pattern)
- string.gsub (s, pattern, repl [, n])
- string.len (s)
- string.lower (s)
- string.match (s, pattern [, init])
- string.pack (fmt, v1, v2, ···)
- string.packsize (fmt)
- string.rep (s, n [, sep])
- string.reverse (s)
- string.sub (s, i [, j])
- string.unpack (fmt, s [, pos])
- string.upper (s)
- 6.4.1 – 匹配模式
- 字符类:
- 模式条目:
- 模式:
- 捕获:
- 6.4.2 – 打包和解包用到的格式串
6.4 – 字符串处理
这个库提供了字符串处理的通用函数。例如字符串查找、子串、模式匹配等。当在 Lua 中对字符串做索引时,第一个字符从 1 开始计算(而不是 C 里的 0 )。索引可以是负数,它指从字符串末尾反向解析。即,最后一个字符在 -1 位置处,等等。
字符串库中的所有函数都在表 string
中。它还将其设置为字符串元表的 __index
域。因此,你可以以面向对象的形式使用字符串函数。例如,string.byte(s,i)
可以写成 s:byte(i)
。
字符串库假定采用单字节字符编码。
string.byte (s [, i [, j]])
返回字符 s[i]
,s[i+1]
, … ,s[j]
的内部数字编码。i
的默认值是 1 ;j
的默认值是 i
。这些索引以函数 string.sub
的规则修正。
数字编码没有必要跨平台。
string.char (···)
接收零或更多的整数。返回和参数数量相同长度的字符串。其中每个字符的内部编码值等于对应的参数值。 数字编码没有必要跨平台。
string.dump (function [, strip])
返回包含有以二进制方式表示的(一个 二进制代码块 )指定函数的字符串。之后可以用 load
调用这个字符串获得该函数的副本(但是绑定新的上值)。如果 strip
为真值,二进制代码块不携带该函数的调试信息(局部变量名,行号,等等。)。
带上值的函数只保存上值的数目。当(再次)加载时,这些上值被更新为 nil 的实例。(你可以使用调试库按你需要的方式来序列化上值,并重载到函数中)
string.find (s, pattern [, init [, plain]])
查找第一个字符串 s
中匹配到的 pattern
(参见 §6.4.1)。如果找到一个匹配,find
会返回 s
中关于它起始及终点位置的索引;否则,返回 nil。第三个可选数字参数 init
指明从哪里开始搜索;默认值为 1 ,同时可以是负值。第四个可选参数 plain
为 true 时,关闭模式匹配机制。此时函数仅做直接的 “查找子串”的操作,而 pattern
中没有字符被看作魔法字符。注意,如果给定了 plain
,就必须写上 init
。
如果在模式中定义了捕获,捕获到的若干值也会在两个索引之后返回。
string.format (formatstring, ···)
返回不定数量参数的格式化版本,格式化串为第一个参数(必须是一个字符串)。格式化字符串遵循 ISO C 函数 sprintf
的规则。不同点在于选项*
, h
, L
, l
, n
,p
不支持,另外还增加了一个选项 q
。q
选项将一个字符串格式化为两个双引号括起,对内部字符做恰当的转义处理的字符串。该字符串可以安全的被 Lua 解释器读回来。例如,调用
- string.format('%q', 'a string with "quotes" and \n new line')
会产生字符串:
- "a string with \"quotes\" and \
- new line"
选项A
and a
(如果有的话),E
, e
, f
,G
, and g
都期待一个对应的数字参数。选项 c
, d
,i
, o
, u
, X
, and x
则期待一个整数。选项 q
期待一个字符串;选项 s
期待一个没有内嵌零的字符串。如果选项 s
对应的参数不是字符串,它会用和tostring
一致的规则转换成字符串。
string.gmatch (s, pattern)
返回一个迭代器函数。每次调用这个函数都会继续以 pattern
(参见 §6.4.1)对 s
做匹配,并返回所有捕获到的值。如果 pattern
中没有指定捕获,则每次捕获整个 pattern
。
下面这个例子会循环迭代字符串 s
中所有的单词,并逐行打印:
- s = "hello world from Lua"
- for w in string.gmatch(s, "%a+") do
- print(w)
- end
下一个例子从指定的字符串中收集所有的键值对key=value
置入一张表:
- t = {}
- s = "from=world, to=Lua"
- for k, v in string.gmatch(s, "(%w+)=(%w+)") do
- t[k] = v
- end
对这个函数来说,模板前开始的 '^
' 不会当成锚点。因为这样会阻止迭代。
string.gsub (s, pattern, repl [, n])
将字符串 s
中,所有的(或是在 n
给出时的前 n
个)pattern
(参见 §6.4.1)都替换成 repl
,并返回其副本。repl
可以是字符串、表、或函数。gsub
还会在第二个返回值返回一共发生了多少次匹配。gsub
这个名字来源于 Global SUBstitution 。
如果 repl
是一个字符串,那么把这个字符串作为替换品。字符 %
是一个转义符:repl
中的所有形式为 %d
的串表示第 d 个捕获到的子串,d 可以是 1 到 9 。串 %0
表示整个匹配。串 %%
表示单个 %
。
如果 repl
是张表,每次匹配时都会用第一个捕获物作为键去查这张表。
如果 repl
是个函数,则在每次匹配发生时都会调用这个函数。所有捕获到的子串依次作为参数传入。
任何情况下,模板中没有设定捕获都看成是捕获整个模板。
如果表的查询结果或函数的返回结果是一个字符串或是个数字,都将其作为替换用串;而在返回 false 或 nil 时不作替换(即保留匹配前的原始串)。
这里有一些用例:
- x = string.gsub("hello world", "(%w+)", "%1 %1")
- --> x="hello hello world world"
- x = string.gsub("hello world", "%w+", "%0 %0", 1)
- --> x="hello hello world"
- x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
- --> x="world hello Lua from"
- x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
- --> x="home = /home/roberto, user = roberto"
- x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
- return load(s)()
- end)
- --> x="4+5 = 9"
- local t = {name="lua", version="5.3"}
- x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
- --> x="lua-5.3.tar.gz"
string.len (s)
接收一个字符串,返回其长度。空串 ""
的长度为 0 。内嵌零也统计在内,因此"a\000bc\000"
的长度为 5 。
string.lower (s)
接收一个字符串,将其中的大写字符都转为小写后返回其副本。其它的字符串不会更改。对大写字符的定义取决于当前的区域设置。
string.match (s, pattern [, init])
在字符串 s
中找到第一个能用 pattern
(参见 §6.4.1)匹配到的部分。如果能找到,match
返回其中的捕获物;否则返回 nil 。如果 pattern
中未指定捕获,返回整个 pattern
捕获到的串。第三个可选数字参数 init
指明从哪里开始搜索;它默认为 1 且可以是负数。
string.pack (fmt, v1, v2, ···)
返回一个打包了(即以二进制形式序列化) v1
, v2
等值的二进制字符串。字符串 fmt
为打包格式(参见 §6.4.2)。
string.packsize (fmt)
返回以指定格式用 string.pack
打包的字符串的长度。格式化字符串中不可以有变长选项 's
' 或 'z
'(参见 §6.4.2)。
string.rep (s, n [, sep])
返回 n
个字符串 s
以字符串 sep
为分割符连在一起的字符串。默认的 sep
值为空字符串(即没有分割符)。如果 n
不是正数则返回空串。
string.reverse (s)
返回字符串 s
的翻转串。
string.sub (s, i [, j])
返回 s
的子串,该子串从 i
开始到 j
为止;i
和 j
都可以为负数。如果不给出 j
,就当它是 -1 (和字符串长度相同)。特别是,调用 string.sub(s,1,j)
可以返回 s
的长度为 j
的前缀串,而 string.sub(s, -i)
返回长度为 i
的后缀串。
如果在对负数索引转义后 i
小于 1 的话,就修正回 1 。如果 j
比字符串的长度还大,就修正为字符串长度。如果在修正之后,i
大于 j
,函数返回空串。
string.unpack (fmt, s [, pos])
返回以格式 fmt
(参见 §6.4.2)打包在字符串 s
(参见 string.pack
)中的值。选项 pos
(默认为 1 )标记了从 s
中哪里开始读起。读完所有的值后,函数返回 s
中第一个未读字节的位置。
string.upper (s)
接收一个字符串,将其中的小写字符都转为大写后返回其副本。其它的字符串不会更改。对小写字符的定义取决于当前的区域设置。
6.4.1 – 匹配模式
Lua 中的匹配模式直接用常规的字符串来描述。它用于模式匹配函数string.find
,string.gmatch
,string.gsub
,string.match
。这一节表述了这些字符串的语法及含义(即它能匹配到什么)。
字符类:
字符类 用于表示一个字符集合。下列组合可用于字符类:
- x: (这里 x 不能是 魔法字符
^$()%.[]*+-?
中的一员)表示字符 x 自身。 .
: (一个点)可表示任何字符。%a
: 表示任何字母。%c
: 表示任何控制字符。%d
: 表示任何数字。%g
: 表示任何除空白符外的可打印字符。%l
: 表示所有小写字母。%p
: 表示所有标点符号。%s
: 表示所有空白字符。%u
: 表示所有大写字母。%w
: 表示所有字母及数字。%x
: 表示所有 16 进制数字符号。%x
: (这里的 x 是任意非字母或数字的字符)表示字符 x。这是对魔法字符转义的标准方法。所有非字母或数字的字符(包括所有标点,也包括非魔法字符)都可以用前置一个 '%
' 放在模式串中表示自身。[set]
: 表示 set 中所有字符的联合。可以以 '-
' 连接,升序书写范围两端的字符来表示一个范围的字符集。上面提到的%
x 形式也可以在 set 中使用表示其中的一个元素。其它出现在 set 中的字符则代表它们自己。例如,[%w]
(或[
%w]
)表示所有的字母数字加下划线),[0-7]
表示 8 进制数字,[0-7%l%-]
表示 8 进制数字加小写字母与 '-
' 字符。 交叉使用类和范围的行为未定义。因此,像[%a-z]
或[a-%%]
这样的模式串没有意义。[^set]
: 表示 set 的补集,其中 set 如上面的解释。 所有单个字母表示的类别(%a
,%c
,等),若将其字母改为大写,均表示对应的补集。例如,%S
表示所有非空格的字符。
如何定义字母、空格、或是其他字符组取决于当前的区域设置。特别注意:[a-z]
未必等价于 %l
。
模式条目:
模式条目 可以是
- 单个字符类匹配该类别中任意单个字符;
- 单个字符类跟一个 '
*
',将匹配零或多个该类的字符。这个条目总是匹配尽可能长的串; - 单个字符类跟一个 '
+
',将匹配一或更多个该类的字符。这个条目总是匹配尽可能长的串; - 单个字符类跟一个 '
-
',将匹配零或更多个该类的字符。和 '*
' 不同,这个条目总是匹配尽可能短的串; - 单个字符类跟一个 '
?
',将匹配零或一个该类的字符。只要有可能,它会匹配一个; %n
,这里的 n 可以从 1 到 9;这个条目匹配一个等于 n 号捕获物(后面有描述)的子串。%bxy
,这里的 x 和 y 是两个明确的字符;这个条目匹配以 x 开始 y 结束,且其中 x 和 y 保持 平衡 的字符串。意思是,如果从左到右读这个字符串,对每次读到一个x 就 +1 ,读到一个 y 就 -1,最终结束处的那个 y 是第一个记数到 0 的 y。举个例子,条目%b()
可以匹配到括号平衡的表达式。%f[set]
,指 边境模式;这个条目会匹配到一个位于 set 内某个字符之前的一个空串,且这个位置的前一个字符不属于 set 。集合 set 的含义如前面所述。匹配出的那个空串之开始和结束点的计算就看成该处有个字符'\0
' 一样。
模式:
模式 指一个模式条目的序列。在模式最前面加上符号 '^
' 将锚定从字符串的开始处做匹配。在模式最后面加上符号 '$
' 将使匹配过程锚定到字符串的结尾。如果 '^
' 和 '$
' 出现在其它位置,它们均没有特殊含义,只表示自身。
捕获:
模式可以在内部用小括号括起一个子模式;这些子模式被称为 捕获物。当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。捕获物以它们左括号的次序来编号。例如,对于模式 "(a(.)%w(%s))"
,字符串中匹配到 "a(.)%w(%s)"
的部分保存在第一个捕获物中(因此是编号 1 );由 ".
" 匹配到的字符是 2 号捕获物,匹配到 "%s*
" 的那部分是 3 号。
作为一个特例,空的捕获 ()
将捕获到当前字符串的位置(它是一个数字)。例如,如果将模式 "()aa()"
作用到字符串"flaaap"
上,将产生两个捕获物:3 和 5 。
6.4.2 – 打包和解包用到的格式串
用于 string.pack
,string.packsize
,string.unpack
的第一个参数。它是一个描述了需要创建或读取的结构之布局。
格式串是由转换选项构成的序列。这些转换选项列在后面:
<
: 设为小端编码>
: 设为大端编码=
: 大小端遵循本地设置![n]
: 将最大对齐数设为n
(默认遵循本地对齐设置)b
: 一个有符号字节 (char
)B
: 一个无符号字节 (char
)h
: 一个有符号short
(本地大小)H
: 一个无符号short
(本地大小)l
: 一个有符号long
(本地大小)L
: 一个无符号long
(本地大小)j
: 一个lua_Integer
J
: 一个lua_Unsigned
T
: 一个size_t
(本地大小)i[n]
: 一个n
字节长(默认为本地大小)的有符号int
I[n]
: 一个n
字节长(默认为本地大小)的无符号int
f
: 一个float
(本地大小)d
: 一个double
(本地大小)n
: 一个lua_Number
cn
:n
字节固定长度的字符串z
: 零结尾的字符串s[n]
: 长度加内容的字符串,其长度编码为一个n
字节(默认是个size_t
) 长的无符号整数。x
: 一个字节的填充Xop
: 按选项op
的方式对齐(忽略它的其它方面)的一个空条目- '
[n]
" 表示一个可选的整数。)除填充、空格、配置项(选项 "xX <=>!
")外,每个选项都关联一个参数(对于string.pack
)或结果(对于string.unpack
)。
对于选项 "!n
", "sn
", "in
", "In
",n
可以是 1 到 16 间的整数。所有的整数选项都将做溢出检查;string.pack
检查提供的值是否能用指定的字长表示;string.unpack
检查读出的值能否置入 Lua 整数中。
任何格式串都假设有一个 "!1=
" 前缀,即最大对齐为 1 (无对齐)且采用本地大小端设置。
对齐行为按如下规则工作:对每个选项,格式化时都会填充一些字节直到数据从一个特定偏移处开始,这个位置是该选项的大小和最大对齐数中较小的那个数的倍数;这个较小值必须是 2 个整数次方。选项 "c
" 及 "z
" 不做对齐处理;选项 "s
" 对对齐遵循其开头的整数。
string.pack
用零去填充(string.unpack
则忽略它)。