💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 6 – 标准库 标准库提供了一些有用的函数, 它们都是直接用 C API 实现的。 其中一些函数提供了原本语言就有的服务 (例如,[`type`](#pdf-type) 与 [`getmetatable`](#pdf-getmetatable)); 另一些提供和“外部”打交道的服务(例如 I/O ); 还有些本可以用 Lua 本身来实现,但在 C 中实现可以满足关键点上的性能需求 (例如 [`table.sort`](#pdf-table.sort))。 所有的库都是直接用 C API 实现的,并以分离的 C 模块形式提供。 目前,Lua 有下列标准库: * 基础库 ([§6.1](#6.1)); * 协程库 ([§6.2](#6.2)); * 包管理库 ([§6.3](#6.3)); * 字符串控制 ([§6.4](#6.4)); * 基础 UTF-8 支持 ([§6.5](#6.5)); * 表控制 ([§6.6](#6.6)); * 数学函数 ([§6.7](#6.7)) (sin ,log 等); * 输入输出 ([§6.8](#6.8)); * 操作系统库 ([§6.9](#6.9)); * 调试库 ([§6.10](#6.10)). 除了基础库和包管理库, 其它库都把自己的函数放在一张全局表的域中, 或是以对象方法的形式提供。 要使用这些库, C 的宿主程序需要先调用一下 [`luaL_openlibs`](#luaL_openlibs) 这个函数, 这样就能打开所有的标准库。 或者宿主程序也可以用 [`luaL_requiref`](#luaL_requiref) 分别打开这些库: `luaopen_base` (基础库), `luaopen_package` (包管理库), `luaopen_coroutine` (协程库), `luaopen_string` (字符串库), `luaopen_utf8` (UTF8 库), `luaopen_table` (表处理库), `luaopen_math` (数学库), `luaopen_io` (I/O 库), `luaopen_os` (操作系统库), `luaopen_debug` (调试库)。 这些函数都定义在 `lualib.h` 中。 ## 6.1 – 基础函数 基础库提供了 Lua 核心函数。 如果你不将这个库包含在你的程序中, 你就需要小心检查程序是否需要自己提供其中一些特性的实现。 ### `assert (v [, message])` 如果其参数 `v` 的值为假(**nil** 或 **false**), 它就调用 [`error`](#pdf-error); 否则,返回所有的参数。 在错误情况时, `message` 指那个错误对象; 如果不提供这个参数,参数默认为 "`assertion failed!`" 。 ### `collectgarbage ([opt [, arg]])` 这个函数是垃圾收集器的通用接口。 通过参数 `opt` 它提供了一组不同的功能: * **"`collect`":** 做一次完整的垃圾收集循环。 这是默认选项。 * **"`stop`":** 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。 * **"`restart`":** 重启垃圾收集器的自动运行。 * **"`count`":** 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。 * **"`step`":** 单步运行垃圾收集器。 步长“大小”由 `arg` 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 **true** 。 * **"`setpause`":** 将 `arg` 设为收集器的 _间歇率_ (参见 [§2.5](#2.5))。 返回 _间歇率_ 的前一个值。 * **"`setstepmul`":** 将 `arg` 设为收集器的 _步进倍率_ (参见 [§2.5](#2.5))。 返回 _步进倍率_ 的前一个值。 * **"`isrunning`":** 返回表示收集器是否在工作的布尔值 (即未被停止)。 ### `dofile ([filename])` 打开该名字的文件,并执行文件中的 Lua 代码块。 不带参数调用时, `dofile` 执行标准输入的内容(`stdin`)。 返回该代码块的所有返回值。 对于有错误的情况,`dofile` 将错误反馈给调用者 (即,`dofile` 没有运行在保护模式下)。 ### `error (message [, level])` 中止上一次保护函数调用, 将错误对象 `message` 返回。 函数 `error` 永远不会返回。 当 message 是一个字符串时,通常 `error` 会把一些有关出错位置的信息附加在消息的前头。 `level` 参数指明了怎样获得出错位置。 对于 level 1 (默认值),出错位置指 `error` 函数调用的位置。 Level 2 将出错位置指向调用 `error`的函数的函数;以此类推。 传入 level 0 可以避免在消息前添加出错位置信息。 ### `_G` 一个全局变量(非函数), 内部储存有全局环境(参见 [§2.2](#2.2))。 Lua 自己不使用这个变量; 改变这个变量的值不会对任何环境造成影响,反之亦然。 ### `getmetatable (object)` 如果 `object` 不包含元表,返回 **nil** 。 否则,如果在该对象的元表中有 `"__metatable"` 域时返回其关联值, 没有时返回该对象的元表。 ### `ipairs (t)` 返回三个值(迭代函数、表 `t` 以及 0 ), 如此,以下代码 ``` for i,v in ipairs(t) do _body_ end ``` 将迭代键值对(`1,t[1]`) ,(`2,t[2]`), ... ,直到第一个空值。 ### `load (chunk [, chunkname [, mode [, env]]])` 加载一个代码块。 如果 `chunk` 是一个字符串,代码块指这个字符串。 如果 `chunk` 是一个函数, `load` 不断地调用它获取代码块的片断。 每次对 `chunk` 的调用都必须返回一个字符串紧紧连接在上次调用的返回串之后。 当返回空串、**nil**、或是不返回值时,都表示代码块结束。 如果没有语法错误, 则以函数形式返回编译好的代码块; 否则,返回 **nil** 加上错误消息。 如果结果函数有上值, `env` 被设为第一个上值。 若不提供此参数,将全局环境替代它。 所有其它上值初始化为 **nil**。 (当你加载主代码块时候,结果函数一定有且仅有一个上值 `_ENV` (参见 [§2.2](#2.2)))。 然而,如果你加载一个用函数(参见 [`string.dump`](#pdf-string.dump), 结果函数可以有任意数量的上值) 创建出来的二进制代码块时,所有的上值都是新创建出来的。 也就是说它们不会和别的任何函数共享。 `chunkname` 在错误消息和调试消息中(参见 [§4.9](#4.9)),用于代码块的名字。 如果不提供此参数,它默认为字符串`chunk` 。 `chunk` 不是字符串时,则为 "`=(load)`" 。 字符串 `mode` 用于控制代码块是文本还是二进制(即预编译代码块)。 它可以是字符串 "`b`" (只能是二进制代码块), "`t`" (只能是文本代码块), 或 "`bt`" (可以是二进制也可以是文本)。 默认值为 "`bt`"。 Lua 不会对二进制代码块做健壮性检查。 恶意构造一个二进制块有可能把解释器弄崩溃。 ### `loadfile ([filename [, mode [, env]]])` 和 [`load`](#pdf-load) 类似, 不过是从文件 `filename` 或标准输入(如果文件名未提供)中获取代码块。 ### `next (table [, index])` 运行程序来遍历表中的所有域。 第一个参数是要遍历的表,第二个参数是表中的某个键。 `next` 返回该键的下一个键及其关联的值。 如果用 **nil** 作为第二个参数调用 `next` 将返回初始键及其关联值。 当以最后一个键去调用,或是以 **nil** 调用一张空表时, `next` 返回 **nil**。 如果不提供第二个参数,将认为它就是 **nil**。 特别指出,你可以用 `next(t)` 来判断一张表是否是空的。 索引在遍历过程中的次序无定义, _即使是数字索引也是这样_。 (如果想按数字次序遍历表,可以使用数字形式的 **for** 。) 当在遍历过程中你给表中并不存在的域赋值, `next` 的行为是未定义的。 然而你可以去修改那些已存在的域。 特别指出,你可以清除一些已存在的域。 ### `pairs (t)` 如果 `t` 有元方法 `__pairs`, 以 `t` 为参数调用它,并返回其返回的前三个值。 否则,返回三个值:[`next`](#pdf-next) 函数, 表 `t`,以及 **nil**。 因此以下代码 ``` for k,v in pairs(t) do _body_ end ``` 能迭代表 `t` 中的所有键值对。 参见函数 [`next`](#pdf-next) 中关于迭代过程中修改表的风险。 ### `pcall (f [, arg1, ···])` 传入参数,以 _保护模式_ 调用函数 `f` 。 这意味着 `f` 中的任何错误不会抛出; 取而代之的是,`pcall` 会将错误捕获到,并返回一个状态码。 第一个返回值是状态码(一个布尔量), 当没有错误时,其为真。 此时,`pcall` 同样会在状态码后返回所有调用的结果。 在有错误时,`pcall` 返回 **false** 加错误消息。 ### `print (···)` 接收任意数量的参数,并将它们的值打印到 `stdout`。 它用 [`tostring`](#pdf-tostring) 函数将每个参数都转换为字符串。 `print` 不用于做格式化输出。仅作为看一下某个值的快捷方式。 多用于调试。 完整的对输出的控制,请使用 [`string.format`](#pdf-string.format) 以及 [`io.write`](#pdf-io.write)。 ### `rawequal (v1, v2)` 在不触发任何元方法的情况下 检查 `v1` 是否和 `v2` 相等。 返回一个布尔量。 ### `rawget (table, index)` 在不触发任何元方法的情况下 获取 `table[index]` 的值。 `table` 必须是一张表; `index` 可以是任何值。 ### `rawlen (v)` 在不触发任何元方法的情况下 返回对象 `v` 的长度。 `v` 可以是表或字符串。 它返回一个整数。 ### `rawset (table, index, value)` 在不触发任何元方法的情况下 将 `table[index]` 设为 `value`。 `table` 必须是一张表, `index` 可以是 **nil** 与 NaN 之外的任何值。 `value` 可以是任何 Lua 值。 这个函数返回 `table`。 ### `select (index, ···)` 如果 `index` 是个数字, 那么返回参数中第 `index` 个之后的部分; 负的数字会从后向前索引(-1 指最后一个参数)。 否则,`index` 必须是字符串 `"#"`, 此时 `select` 返回参数的个数。 ### `setmetatable (table, metatable)` 给指定表设置元表。 (你不能在 Lua 中改变其它类型值的元表,那些只能在 C 里做。) 如果 `metatable` 是 **nil**, 将指定表的元表移除。 如果原来那张元表有 `"__metatable"` 域,抛出一个错误。 这个函数返回 `table`。 ### `tonumber (e [, base])` 如果调用的时候没有 `base`, `tonumber` 尝试把参数转换为一个数字。 如果参数已经是一个数字,或是一个可以转换为数字的字符串, `tonumber` 就返回这个数字; 否则返回 **nil**。 字符串的转换结果可能是整数也可能是浮点数, 这取决于 Lua 的转换文法(参见 [§3.1](#3.1))。 (字符串可以有前置和后置的空格,可以带符号。) 当传入 `base` 调用它时, `e` 必须是一个以该进制表示的整数字符串。 进制可以是 2 到 36 (包含 2 和 36)之间的任何整数。 大于 10 进制时,字母 '`A`' (大小写均可)表示 10 , '`B`' 表示 11,依次到 '`Z`' 表示 35 。 如果字符串 `e` 不是该进制下的合法数字, 函数返回 **nil**。 ### `tostring (v)` 可以接收任何类型,它将其转换为人可阅读的字符串形式。 浮点数总被转换为浮点数的表现形式(小数点形式或是指数形式)。 (如果想完全控制数字如何被转换,可以使用 [`string.format`](#pdf-string.format)。) 如果 `v` 有 `"__tostring"` 域的元表, `tostring` 会以 `v` 为参数调用它。 并用它的结果作为返回值。 ### `type (v)` 将参数的类型编码为一个字符串返回。 函数可能的返回值有 "`nil`" (一个字符串,而不是 **nil** 值), "`number`", "`string`", "`boolean`", "`table`", "`function`", "`thread`", "`userdata`"。 ### `_VERSION` 一个包含有当前解释器版本号的全局变量(并非函数)。 当前这个变量的值为 "`Lua 5.3`"。 ### `xpcall (f, msgh [, arg1, ···])` 这个函数和 [`pcall`](#pdf-pcall) 类似。 不过它可以额外设置一个消息处理器 `msgh`。 ## 6.2 – 协程管理 关于协程的操作作为基础库的一个子库, 被放在一个独立表 `coroutine` 中。 协程的介绍参见 [§2.6](#2.6) 。 ### `coroutine.create (f)` 创建一个主体函数为 `f` 的新协程。 `f` 必须是一个 Lua 的函数。 返回这个新协程,它是一个类型为 `"thread"` 的对象。 ### `coroutine.isyieldable ()` 如果正在运行的协程可以让出,则返回真。 不在主线程中或不在一个无法让出的 C 函数中时,当前协程是可让出的。 ### `coroutine.resume (co [, val1, ···])` 开始或继续协程 `co` 的运行。 当你第一次延续一个协程,它会从主体函数处开始运行。 `val1`, ... 这些值会以参数形式传入主体函数。 如果该协程被让出,`resume` 会重新启动它; `val1`, ... 这些参数会作为让出点的返回值。 如果协程运行起来没有错误, `resume` 返回 **true** 加上传给 `yield` 的所有值 (当协程让出), 或是主体函数的所有返回值(当协程中止)。 如果有任何错误发生, `resume` 返回 **false** 加错误消息。 ### `coroutine.running ()` 返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。 ### `coroutine.status (co)` 以字符串形式返回协程 `co` 的状态: 当协程正在运行(它就是调用 `status` 的那个) ,返回 `"running"`; 如果协程调用 `yield` 挂起或是还没有开始运行,返回 `"suspended"`; 如果协程是活动的,都并不在运行(即它正在延续其它协程),返回 `"normal"`; 如果协程运行完主体函数或因错误停止,返回 `"dead"`。 ### `coroutine.wrap (f)` 创建一个主体函数为 `f` 的新协程。 `f` 必须是一个 Lua 的函数。 返回一个函数, 每次调用该函数都会延续该协程。 传给这个函数的参数都会作为 `resume` 的额外参数。 和 `resume` 返回相同的值, 只是没有第一个布尔量。 如果发生任何错误,抛出这个错误。 ### `coroutine.yield (···)` 挂起正在调用的协程的执行。 传递给 `yield` 的参数都会转为 `resume` 的额外返回值。 ## 6.3 – 模块 包管理库提供了从 Lua 中加载模块的基础库。 只有一个导出函数直接放在全局环境中: [`require`](#pdf-require)。 所有其它的部分都导出在表 `package` 中。 ### `require (modname)` 加载一个模块。 这个函数首先查找 [`package.loaded`](#pdf-package.loaded) 表, 检测 `modname` 是否被加载过。 如果被加载过,`require` 返回 `package.loaded[modname]` 中保存的值。 否则,它试着为模块寻找 _加载器_ 。 `require` 遵循 [`package.searchers`](#pdf-package.searchers) 序列的指引来查找加载器。 如果改变这个序列,我们可以改变 `require` 如何查找一个模块。 下列说明基于 [`package.searchers`](#pdf-package.searchers) 的默认配置。 首先 `require` 查找 `package.preload[modname]` 。 如果这里有一个值,这个值(必须是一个函数)就是那个加载器。 否则 `require` 使用 Lua 加载器去查找 [`package.path`](#pdf-package.path) 的路径。 如果查找失败,接着使用 C 加载器去查找 [`package.cpath`](#pdf-package.cpath) 的路径。 如果都失败了,再尝试 _一体化_ 加载器 (参见 [`package.searchers`](#pdf-package.searchers))。 每次找到一个加载器,`require` 都用两个参数调用加载器: `modname` 和一个在获取加载器过程中得到的参数。 (如果通过查找文件得到的加载器,这个额外参数是文件名。) 如果加载器返回非空值, `require` 将这个值赋给 `package.loaded[modname]`。 如果加载器没能返回一个非空值用于赋给 `package.loaded[modname]`, `require` 会在那里设入 **true** 。 无论是什么情况,`require` 都会返回 `package.loaded[modname]` 的最终值。 如果在加载或运行模块时有错误, 或是无法为模块找到加载器, `require` 都会抛出错误。 ### `package.config` 一个描述有一些为包管理准备的编译期配置信息的串。 这个字符串由一系列行构成: * 第一行是目录分割串。 对于 Windows 默认是 '`\`' ,对于其它系统是 '`/`' 。 * 第二行是用于路径中的分割符。默认值是 '`;`' 。 * 第三行是用于标记模板替换点的字符串。 默认是 '`?`' 。 * 第四行是在 Windows 中将被替换成执行程序所在目录的路径的字符串。 默认是 '`!`' 。 * 第五行是一个记号,该记号之后的所有文本将在构建 `luaopen_` 函数名时被忽略掉。 默认是 '`-`'。 ### `package.cpath` 这个路径被 [`require`](#pdf-require) 在 C 加载器中做搜索时用到。 Lua 用和初始化 Lua 路径 [`package.path`](#pdf-package.path) 相同的方式初始化 C 路径 [`package.cpath`](#pdf-package.cpath) 。 它会使用环境变量 `LUA_CPATH_5_3` 或 环境变量 `LUA_CPATH` 初始化。 要么就采用 `luaconf.h` 中定义的默认路径。 ### `package.loaded` 用于 [`require`](#pdf-require) 控制哪些模块已经被加载的表。 当你请求一个 `modname` 模块,且 `package.loaded[modname]` 不为假时, [`require`](#pdf-require) 简单返回储存在内的值。 这个变量仅仅是对真正那张表的引用; 改变这个值并不会改变 [`require`](#pdf-require) 使用的表。 ### `package.loadlib (libname, funcname)` 让宿主程序动态链接 C 库 `libname` 。 当 `funcname` 为 "`*`", 它仅仅连接该库,让库中的符号都导出给其它动态链接库使用。 否则,它查找库中的函数 `funcname` ,以 C 函数的形式返回这个函数。 因此,`funcname` 必须遵循原型 [`lua_CFunction`](#lua_CFunction) (参见 [`lua_CFunction`](#lua_CFunction))。 这是一个低阶函数。 它完全绕过了包模块系统。 和 [`require`](#pdf-require) 不同, 它不会做任何路径查询,也不会自动加扩展名。 `libname` 必须是一个 C 库需要的完整的文件名,如果有必要,需要提供路径和扩展名。 `funcname` 必须是 C 库需要的准确名字 (这取决于使用的 C 编译器和链接器)。 这个函数在标准 C 中不支持。 因此,它只在部分平台有效 ( Windows ,Linux ,Mac OS X, Solaris, BSD, 加上支持 `dlfcn` 标准的 Unix 系统)。 ### `package.path` 这个路径被 [`require`](#pdf-require) 在 Lua 加载器中做搜索时用到。 在启动时,Lua 用环境变量 `LUA_PATH_5_3` 或环境变量 `LUA_PATH` 来初始化这个变量。 或采用 `luaconf.h` 中的默认路径。 环境变量中出现的所有 "`;;`" 都会被替换成默认路径。 ### `package.preload` 保存有一些特殊模块的加载器 (参见 [`require`](#pdf-require))。 这个变量仅仅是对真正那张表的引用; 改变这个值并不会改变 [`require`](#pdf-require) 使用的表。 ### `package.searchers` 用于 [`require`](#pdf-require) 控制如何加载模块的表。 这张表内的每一项都是一个 _查找器函数_。 当查找一个模块时, [`require`](#pdf-require) 按次序调用这些查找器, 并传入模块名([`require`](#pdf-require) 的参数)作为唯一的一个参数。 此函数可以返回另一个函数(模块的 _加载器_)加上另一个将传递给这个加载器的参数。 或是返回一个描述为何没有找到这个模块的字符串 (或是返回 **nil** 什么也不想说)。 Lua 用四个查找器函数初始化这张表。 第一个查找器就是简单的在 [`package.preload`](#pdf-package.preload) 表中查找加载器。 第二个查找器用于查找 Lua 库的加载库。 它使用储存在 [`package.path`](#pdf-package.path) 中的路径来做查找工作。 查找过程和函数 [`package.searchpath`](#pdf-package.searchpath) 描述的一致。 第三个查找器用于查找 C 库的加载库。 它使用储存在 [`package.cpath`](#pdf-package.path) 中的路径来做查找工作。 同样, 查找过程和函数 [`package.searchpath`](#pdf-package.searchpath) 描述的一致。 例如,如果 C 路径是这样一个字符串 ``` "./?.so;./?.dll;/usr/local/?/init.so" ``` 查找器查找模块 `foo` 会依次尝试打开文件 `./foo.so`,`./foo.dll`, 以及 `/usr/local/foo/init.so`。 一旦它找到一个 C 库, 查找器首先使用动态链接机制连接该库。 然后尝试在该库中找到可以用作加载器的 C 函数。 这个 C 函数的名字是 "`luaopen_`" 紧接模块名的字符串, 其中字符串中所有的下划线都会被替换成点。 此外,如果模块名中有横线, 横线后面的部分(包括横线)都被去掉。 例如,如果模块名为 `a.b.c-v2.1`, 函数名就是 `luaopen_a_b_c`。 第四个搜索器是 _一体化加载器_。 它从 C 路径中查找指定模块的根名字。 例如,当请求 `a.b.c` 时, 它将查找 `a` 这个 C 库。 如果找得到,它会在里面找子模块的加载函数。 在我们的例子中,就是找 `luaopen_a_b_c`。 利用这个机制,可以把若干 C 子模块打包进单个库。 每个子模块都可以有原本的加载函数名。 除了第一个(预加载)搜索器外,每个搜索器都会返回 它找到的模块的文件名。 这和 [`package.searchpath`](#pdf-package.searchpath) 的返回值一样。 第一个搜索器没有返回值。 ### `package.searchpath (name, path [, sep [, rep]])` 在指定 `path` 中搜索指定的 `name` 。 路径是一个包含有一系列以分号分割的 _模板_ 构成的字符串。 对于每个模板,都会用 `name` 替换其中的每个问号(如果有的话)。 且将其中的 `sep` (默认是点)替换为 `rep` (默认是系统的目录分割符)。 然后尝试打开这个文件名。 例如,如果路径是字符串 ``` "./?.lua;./?.lc;/usr/local/?/init.lua" ``` 搜索 `foo.a` 这个名字将 依次尝试打开文件 `./foo/a.lua` , `./foo/a.lc` ,以及 `/usr/local/foo/a/init.lua`。 返回第一个可以用读模式打开(并马上关闭该文件)的文件的名字。 如果不存在这样的文件,返回 **nil** 加上错误消息。 (这条错误消息列出了所有尝试打开的文件名。) ## 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`](#pdf-string.sub) 的规则修正。 数字编码没有必要跨平台。 ### `string.char (···)` 接收零或更多的整数。 返回和参数数量相同长度的字符串。 其中每个字符的内部编码值等于对应的参数值。 数字编码没有必要跨平台。 ### `string.dump (function [, strip])` 返回包含有以二进制方式表示的(一个 _二进制代码块_ )指定函数的字符串。 之后可以用 [`load`](#pdf-load) 调用这个字符串获得 该函数的副本(但是绑定新的上值)。 如果 `strip` 为真值, 二进制代码块不携带该函数的调试信息 (局部变量名,行号,等等。)。 带上值的函数只保存上值的数目。 当(再次)加载时,这些上值被更新为 **nil** 的实例。 (你可以使用调试库按你需要的方式来序列化上值,并重载到函数中) ### `string.find (s, pattern [, init [, plain]])` 查找第一个字符串 `s` 中匹配到的 `pattern` (参见 [§6.4.1](#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`](#pdf-tostring) 一致的规则转换成字符串。 ### `string.gmatch (s, pattern)` 返回一个迭代器函数。 每次调用这个函数都会继续以 `pattern` (参见 [§6.4.1](#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](#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](#6.4.1))匹配到的部分。 如果能找到,`match` 返回其中的捕获物; 否则返回 **nil** 。 如果 `pattern` 中未指定捕获, 返回整个 `pattern` 捕获到的串。 第三个可选数字参数 `init` 指明从哪里开始搜索; 它默认为 1 且可以是负数。 ### `string.pack (fmt, v1, v2, ···)` 返回一个打包了(即以二进制形式序列化) `v1`, `v2` 等值的二进制字符串。 字符串 `fmt` 为打包格式(参见 [§6.4.2](#6.4.2))。 ### `string.packsize (fmt)` 返回以指定格式用 [`string.pack`](#pdf-string.pack) 打包的字符串的长度。 格式化字符串中不可以有变长选项 '`s`' 或 '`z`' (参见 [§6.4.2](#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](#6.4.2)) 打包在字符串 `s` (参见 [`string.pack`](#pdf-string.pack)) 中的值。 选项 `pos`(默认为 1 )标记了从 `s` 中哪里开始读起。 读完所有的值后,函数返回 `s` 中第一个未读字节的位置。 ### `string.upper (s)` 接收一个字符串,将其中的小写字符都转为大写后返回其副本。 其它的字符串不会更改。 对小写字符的定义取决于当前的区域设置。 ### 6.4.1 – 匹配模式 Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 [`string.find`](#pdf-string.find), [`string.gmatch`](#pdf-string.gmatch), [`string.gsub`](#pdf-string.gsub), [`string.match`](#pdf-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_ 号捕获物(后面有描述)的子串。 * `%b_xy_`, 这里的 _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`](#pdf-string.pack), [`string.packsize`](#pdf-string.packsize), [`string.unpack`](#pdf-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` * **`c_n_`:** `n`字节固定长度的字符串 * **`z`:** 零结尾的字符串 * **`s[_n_]`:** 长度加内容的字符串,其长度编码为一个 `n` 字节(默认是个 `size_t`) 长的无符号整数。 * **`x`:** 一个字节的填充 * **`X_op_`:** 按选项 `op` 的方式对齐(忽略它的其它方面)的一个空条目 * **'':** (空格)忽略 ( "`[_n_]`" 表示一个可选的整数。) 除填充、空格、配置项(选项 "`xX <=>!`")外, 每个选项都关联一个参数(对于 [`string.pack`](#pdf-string.pack)) 或结果(对于 [`string.unpack`](#pdf-string.unpack))。 对于选项 "`!_n_`", "`s_n_`", "`i_n_`", "`I_n_`", `n` 可以是 1 到 16 间的整数。 所有的整数选项都将做溢出检查; [`string.pack`](#pdf-string.pack) 检查提供的值是否能用指定的字长表示; [`string.unpack`](#pdf-string.unpack) 检查读出的值能否置入 Lua 整数中。 任何格式串都假设有一个 "`!1=`" 前缀, 即最大对齐为 1 (无对齐)且采用本地大小端设置。 对齐行为按如下规则工作: 对每个选项,格式化时都会填充一些字节直到数据从一个特定偏移处开始, 这个位置是该选项的大小和最大对齐数中较小的那个数的倍数; 这个较小值必须是 2 个整数次方。 选项 "`c`" 及 "`z`" 不做对齐处理; 选项 "`s`" 对对齐遵循其开头的整数。 [`string.pack`](#pdf-string.pack) 用零去填充 ([`string.unpack`](#pdf-string.unpack) 则忽略它)。 ## 6.5 – UTF-8 支持 这个库提供了对 UTF-8 编码的基础支持。 所有的函数都放在表 `utf8` 中。 此库不提供除编码处理之外的任何 Unicode 支持。 所有需要了解字符含义的操作,比如字符分类,都不在此范畴。 除非另有说明, 当一个函数需要一个字节位置的参数时, 都假定这个位置要么从字节序列的开始计算, 要么从字符串长度加一的位置算。 和字符串库一样,负的索引从字符串末尾计起。 ### `utf8.char (···)` 接收零或多个整数, 将每个整数转换成对应的 UTF-8 字节序列,并返回这些序列连接到一起的字符串。 ### `utf8.charpattern` 用于精确匹配到一个 UTF-8 字节序列的模式(是一个字符串,并非函数)"`[\0-\x7F\xC2-\xF4][\x80-\xBF]*`" (参见 [§6.4.1](#6.4.1))。 它假定处理的对象是一个合法的 UTF-8 字符串。 ### `utf8.codes (s)` 返回一系列的值,可以让 ``` for p, c in utf8.codes(s) do _body_ end ``` 迭代出字符串 `s` 中所有的字符。 这里的 `p` 是位置(按字节数)而 `c` 是每个字符的编号。 如果处理到一个不合法的字节序列,将抛出一个错误。 ### `utf8.codepoint (s [, i [, j]])` 一整数形式返回 `s` 中 从位置 `i` 到 `j` 间(包括两端) 所有字符的编号。 默认的 `i` 为 1 ,默认的 `j` 为 `i`。 如果碰上不合法的字节序列,抛出一个错误。 ### `utf8.len (s [, i [, j]])` 返回字符串 `s` 中 从位置 `i` 到 `j` 间 (包括两端) UTF-8 字符的个数。 默认的 `i` 为 1 ,默认的 `j` 为 -1 。 如果它找到任何不合法的字节序列, 返回假值加上第一个不合法字节的位置。 ### `utf8.offset (s, n [, i])` 返回编码在 `s` 中的第 `n` 个字符的开始位置(按字节数) (从位置 `i` 处开始统计)。 负 `n` 则取在位置 `i` 前的字符。 当 `n` 是非负数时,默认的 `i` 是 1, 否则默认为 `#s + 1`。 因此,`utf8.offset(s, -n)` 取字符串的倒数第 `n` 个字符的位置。 如果指定的字符不在其中或在结束点之后,函数返回 **nil**。 作为特例,当 `n` 等于 0 时, 此函数返回含有 `s` 第 `i` 字节的那个字符的开始位置。 这个函数假定 `s` 是一个合法的 UTF-8 字符串。 ## 6.6 – 表处理 这个库提供了表处理的通用函数。 所有函数都放在表 `table` 中。 记住,无论何时,若一个操作需要取表的长度, 这张表必须是一个真序列,或是拥有 `__len` 元方法 (参见 [§3.4.7](#3.4.7) )。 所有的函数都忽略传入参数的那张表中的非数字键。 ### `table.concat (list [, sep [, i [, j]]])` 提供一个列表,其所有元素都是字符串或数字,返回字符串 `list[i]..sep..list[i+1] ··· sep..list[j]`。 `sep` 的默认值是空串, `i` 的默认值是 1 , `j` 的默认值是 `#list` 。 如果 `i` 比 `j` 大,返回空串。 ### `table.insert (list, [pos,] value)` 在 `list` 的位置 `pos` 处插入元素 `value` , 并后移元素 `list[pos], list[pos+1], ···, list[#list]` 。 `pos` 的默认值为 `#list+1` , 因此调用 `table.insert(t,x)` 会将 `x` 插在列表 `t` 的末尾。 ### `table.move (a1, f, e, t [,a2])` 将元素从表 `a1` 移到表 `a2`。 这个函数做了次等价于后面这个多重赋值的等价操作: `a2[t],··· = a1[f],···,a1[e]`。 `a2` 的默认值为 `a1`。 目标区间可以和源区间重叠。 索引 `f` 必须是正数。 ### `table.pack (···)` 返回用所有参数以键 1,2, 等填充的新表, 并将 "`n`" 这个域设为参数的总数。 注意这张返回的表不一定是一个序列。 ### `table.remove (list [, pos])` 移除 `list` 中 `pos` 位置上的元素,并返回这个被移除的值。 当 `pos` 是在 1 到 `#list` 之间的整数时, 它向前移动元素 `list[pos+1], list[pos+2], ···, list[#list]` 并删除元素 `list[#list]`; 索引 `pos` 可以是 `#list + 1` ,或在 `#list` 为 0 时可以是 0 ; 在这些情况下,函数删除元素 `list[pos]`。 `pos` 默认为 `#list`, 因此调用 `table.remove(l)` 将移除表 `l` 的最后一个元素。 ### `table.sort (list [, comp])` 在表内从 `list[1]` 到 `list[#list]` _原地_ 对其间元素按指定次序排序。 如果提供了 `comp` , 它必须是一个可以接收两个列表内元素为参数的函数。 当第一个元素需要排在第二个元素之前时,返回真 (因此 `not comp(list[i+1],list[i])` 在排序结束后将为真)。 如果没有提供 `comp`, 将使用标准 Lua 操作 `<` 作为替代品。 排序算法并不稳定; 即当两个元素次序相等时,它们在排序后的相对位置可能会改变。 ### `table.unpack (list [, i [, j]])` 返回列表中的元素。 这个函数等价于 ``` return list[i], list[i+1], ···, list[j] ``` `i` 默认为 1 ,`j` 默认为 `#list`。 ## 6.7 – 数学函数 这个库提供了基本的数学函数。 所以函数都放在表 `math` 中。 注解有 "`integer/float`" 的函数会对整数参数返回整数结果, 对浮点(或混合)参数返回浮点结果。 圆整函数([`math.ceil`](#pdf-math.ceil), [`math.floor`](#pdf-math.floor), [`math.modf`](#pdf-math.modf)) 在结果在整数范围内时返回整数,否则返回浮点数。 ### `math.abs (x)` 返回 `x` 的绝对值。(integer/float) ### `math.acos (x)` 返回 `x` 的反余弦值(用弧度表示)。 ### `math.asin (x)` 返回 `x` 的反正弦值(用弧度表示)。 ### `math.atan (y [, x])` 返回 `y/x` 的反正切值(用弧度表示)。 它会使用两个参数的符号来找到结果落在哪个象限中。 (即使 `x` 为零时,也可以正确的处理。) 默认的 `x` 是 1 , 因此调用 `math.atan(y)` 将返回 `y` 的反正切值。 ### `math.ceil (x)` 返回不小于 `x` 的最小整数值。 ### `math.cos (x)` 返回 `x` 的余弦(假定参数是弧度)。 ### `math.deg (x)` 将角 `x` 从弧度转换为角度。 ### `math.exp (x)` 返回 _e<sup>x</sup>_ 的值 (`e` 为自然对数的底)。 ### `math.floor (x)` 返回不大于 `x` 的最大整数值。 ### `math.fmod (x, y)` 返回 `x` 除以 `y`,将商向零圆整后的余数。 (integer/float) ### `math.huge` 浮点数 `HUGE_VAL`, 这个数比任何数字值都大。 ### `math.log (x [, base])` 返回以指定底的 `x` 的对数。 默认的 `base` 是 _e_ (因此此函数返回 `x` 的自然对数)。 ### `math.max (x, ···)` 返回参数中最大的值, 大小由 Lua 操作 `<` 决定。 (integer/float) ### `math.maxinteger` 最大值的整数。 ### `math.min (x, ···)` 返回参数中最小的值, 大小由 Lua 操作 `<` 决定。 (integer/float) ### `math.mininteger` 最小值的整数。 ### `math.modf (x)` 返回 `x` 的整数部分和小数部分。 第二个结果一定是浮点数。 ### `math.pi` _π_ 的值。 ### `math.rad (x)` 将角 `x` 从角度转换为弧度。 ### `math.random ([m [, n]])` 当不带参数调用时, 返回一个 _[0,1)_ 区间内一致分布的浮点伪随机数。 当以两个整数 `m` 与 `n` 调用时, `math.random` 返回一个 _[m, n]_ 区间 内一致分布的整数伪随机数。 (值 _m-n_ 不能是负数,且必须在 Lua 整数的表示范围内。) 调用 `math.random(n)` 等价于 `math.random(1,n)`。 这个函数是对 C 提供的位随机数函数的封装。 对其统计属性不作担保。 ### `math.randomseed (x)` 把 `x` 设为伪随机数发生器的“种子”: 相同的种子产生相同的随机数列。 ### `math.sin (x)` 返回 `x` 的正弦值(假定参数是弧度)。 ### `math.sqrt (x)` 返回 `x` 的平方根。 (你也可以使用乘方 `x^0.5` 来计算这个值。) ### `math.tan (x)` 返回 `x` 的正切值(假定参数是弧度)。 ### `math.tointeger (x)` 如果 `x` 可以转换为一个整数, 返回该整数。 否则返回 **nil**。 ### `math.type (x)` 如果 `x` 是整数,返回 "`integer`", 如果它是浮点数,返回 "`float`", 如果 `x` 不是数字,返回 **nil**。 ### `math.ult (m, n)` 如果整数 `m` 和 `n` 以无符号整数形式比较, `m` 在 `n` 之下,返回布尔真否则返回假。 ## 6.8 – 输入输出库 I/O 库提供了两套不同风格的文件处理接口。 第一种风格使用隐式的文件句柄; 它提供设置默认输入文件及默认输出文件的操作, 所有的输入输出操作都针对这些默认文件。 第二种风格使用显式的文件句柄。 当使用隐式文件句柄时, 所有的操作都由表 `io` 提供。 若使用显式文件句柄, [`io.open`](#pdf-io.open) 会返回一个文件句柄,且所有的操作都由该文件句柄的方法来提供。 表 `io` 中也提供了三个 和 C 中含义相同的预定义文件句柄: `io.stdin`, `io.stdout`, 以及 `io.stderr`。 I/O 库永远不会关闭这些文件。 除非另有说明, I/O 函数在出错时都返回 **nil** (第二个返回值为错误消息,第三个返回值为系统相关的错误码)。 成功时返回与 **nil** 不同的值。 在非 POSIX 系统上, 根据错误码取出错误消息的过程可能并非线程安全的, 因为这使用了 C 的全局变量 `errno` 。 ### `io.close ([file])` 等价于 `file:close()`。 不给出 `file` 时将关闭默认输出文件。 ### `io.flush ()` 等价于 `io.output():flush()`。 ### `io.input ([file])` 用文件名调用它时,(以文本模式)来打开该名字的文件, 并将文件句柄设为默认输入文件。 如果用文件句柄去调用它, 就简单的将该句柄设为默认输入文件。 如果调用时不传参数,它返回当前的默认输入文件。 在出错的情况下,函数抛出错误而不是返回错误码。 ### `io.lines ([filename ···])` 以读模式打开指定的文件名并返回一个迭代函数。 此迭代函数的工作方式和用一个已打开的文件去调用 `file:lines(···)` 得到的迭代器相同。 当迭代函数检测到文件结束, 它不返回值(让循环结束)并自动关闭文件。 调用 `io.lines()` (不传文件名) 等价于 `io.input():lines("*l")`; 即,它将按行迭代标准输入文件。 在此情况下,循环结束后它不会关闭文件。 在出错的情况下,函数抛出错误而不是返回错误码。 ### `io.open (filename [, mode])` 这个函数用字符串 `mode` 指定的模式打开一个文件。 返回新的文件句柄。 当出错时,返回 **nil** 加错误消息。 `mode` 字符串可以是下列任意值: * **"`r`":** 读模式(默认); * **"`w`":** 写模式; * **"`a`":** 追加模式; * **"`r+`":** 更新模式,所有之前的数据都保留; * **"`w+`":** 更新模式,所有之前的数据都删除; * **"`a+`":** 追加更新模式,所有之前的数据都保留,只允许在文件尾部做写入。 `mode` 字符串可以在最后加一个 '`b`' , 这会在某些系统上以二进制方式打开文件。 ### `io.output ([file])` 类似于 [`io.input`](#pdf-io.input)。 不过都针对默认输出文件操作。 ### `io.popen (prog [, mode])` 这个函数和系统有关,不是所有的平台都提供。 用一个分离进程开启程序 `prog`, 返回的文件句柄可用于从这个程序中读取数据 (如果 `mode` 为 `"r"`,这是默认值) 或是向这个程序写入输入(当 `mode` 为 `"w"` 时)。 ### `io.read (···)` 等价于 `io.input():read(···)`。 ### `io.tmpfile ()` 返回一个临时文件的句柄。 这个文件以更新模式打开,在程序结束时会自动删除。 ### `io.type (obj)` 检查 `obj` 是否是合法的文件句柄。 如果 `obj` 它是一个打开的文件句柄,返回字符串 `"file"`。 如果 `obj` 是一个关闭的文件句柄,返回字符串 `"closed file"`。 如果 `obj` 不是文件句柄,返回 **nil** 。 ### `io.write (···)` 等价于 `io.output():write(···)`。 ### `file:close ()` 关闭 `file`。 注意,文件在句柄被垃圾回收时会自动关闭, 但是多久以后发生,时间不可预期的。 当关闭用 [`io.popen`](#pdf-io.popen) 创建出来的文件句柄时, [`file:close`](#pdf-file:close) 返回 [`os.execute`](#pdf-os.execute) 会返回的一样的值。 ### `file:flush ()` 将写入的数据保存到 `file` 中。 ### `file:lines (···)` 返回一个迭代器函数, 每次调用迭代器时,都从文件中按指定格式读数据。 如果没有指定格式,使用默认值 "`l`" 。 看一个例子 ``` for c in file:lines(1) do _body_ end ``` 会从文件当前位置开始,中不断读出字符。 和 [`io.lines`](#pdf-io.lines) 不同, 这个函数在循环结束后不会关闭文件。 在出错的情况下,函数抛出错误而不是返回错误码。 ### `file:read (···)` 读文件 `file`, 指定的格式决定了要读什么。 对于每种格式,函数返回读出的字符对应的字符串或数字。 若不能以该格式对应读出数据则返回 **nil**。 (对于最后这种情况, 函数不会读出后续的格式。) 当调用时不传格式,它会使用默认格式读下一行(见下面描述)。 提供的格式有 * **"`n`":** 读取一个数字,根据 Lua 的转换文法,可能返回浮点数或整数。 (数字可以有前置或后置的空格,以及符号。) 只要能构成合法的数字,这个格式总是去读尽量长的串; 如果读出来的前缀无法构成合法的数字 (比如空串,"`0x`" 或 "`3.4e-`"), 就中止函数运行,返回 **nil**。 * **"`i`":** 读取一个整数,返回整数值。 * **"`a`":** 从当前位置开始读取整个文件。 如果已在文件末尾,返回空串。 * **"`l`":** 读取一行并忽略行结束标记。 当在文件末尾时,返回 **nil** 这是默认格式。 * **"`L`":** 读取一行并保留行结束标记(如果有的话), 当在文件末尾时,返回 **nil**。 * **_number_:** 读取一个不超过这个数量字节数的字符串。 当在文件末尾时,返回 **nil**。 如果 `number` 为零, 它什么也不读,返回一个空串。 当在文件末尾时,返回 **nil**。 格式 "`l`" 和 "`L`" 只能用于文本文件。 ### `file:seek ([whence [, offset]])` 设置及获取基于文件开头处计算出的位置。 设置的位置由 `offset` 和 `whence` 字符串 `whence` 指定的基点决定。基点可以是: * **"`set`":** 基点为 0 (文件开头); * **"`cur`":** 基点为当前位置了; * **"`end`":** 基点为文件尾; 当 `seek` 成功时,返回最终从文件开头计算起的文件的位置。 当 `seek` 失败时,返回 **nil** 加上一个错误描述字符串。 `whence` 的默认值是 `"cur"`, `offset` 默认为 0 。 因此,调用 `file:seek()` 可以返回文件当前位置,并不改变它; 调用 `file:seek("set")` 将位置设为文件开头(并返回 0); 调用 `file:seek("end")` 将位置设到文件末尾,并返回文件大小。 ### `file:setvbuf (mode [, size])` 设置输出文件的缓冲模式。 有三种模式: * **"`no`":** 不缓冲;输出操作立刻生效。 * **"`full`":** 完全缓冲;只有在缓存满或当你显式的对文件调用 `flush`(参见 [`io.flush`](#pdf-io.flush)) 时才真正做输出操作。 * **"`line`":** 行缓冲; 输出将到每次换行前, 对于某些特殊文件(例如终端设备)缓冲到任何输入前。 对于后两种情况,`size` 以字节数为单位 指定缓冲区大小。 默认会有一个恰当的大小。 ### `file:write (···)` 将参数的值逐个写入 `file`。 参数必须是字符串或数字。 成功时,函数返回 `file`。 否则返回 **nil** 加错误描述字符串。 ## 6.9 – 操作系统库 这个库都通过表 `os` 实现。 ### `os.clock ()` 返回程序使用的按秒计 CPU 时间的近似值。 ### `os.date ([format [, time]])` 返回一个包含日期及时刻的字符串或表。 格式化方法取决于所给字符串 `format`。 如果提供了 `time` 参数, 格式化这个时间 (这个值的含义参见 [`os.time`](#pdf-os.time) 函数)。 否则,`date` 格式化当前时间。 如果 `format` 以 '`!`' 打头, 日期以协调世界时格式化。 在这个可选字符项之后, 如果 `format` 为字符串 "`*t`", `date` 返回有后续域的表: `year` (四位数字),`month` (1–12),`day` (1–31), `hour` (0–23),`min` (0–59),`sec` (0–61), `wday` (星期几,星期天为 1 ), `yday` (当年的第几天), 以及 `isdst` (夏令时标记,一个布尔量)。 对于最后一个域,如果该信息不提供的话就不存在。 如果 `format` 并非 "`*t`", `date` 以字符串形式返回, 格式化方法遵循 ISO C 函数 `strftime` 的规则。 如果不传参数调用, `date` 返回一个合理的日期时间串, 格式取决于宿主程序以及当前的区域设置 (即,`os.date()` 等价于 `os.date("%c")`)。 在非 POSIX 系统上, 由于这个函数依赖 C 函数 `gmtime` 和 `localtime`, 它可能并非线程安全的。 ### `os.difftime (t2, t1)` 返回以秒计算的时刻 `t1` 到 `t2` 的差值。 (这里的时刻是由 [`os.time`](#pdf-os.time) 返回的值)。 在 POSIX,Windows,和其它一些系统中,这个值就等于 `t2`_-_`t1`。 ### `os.execute ([command])` 这个函数等价于 ISO C 函数 `system`。 它调用系统解释器执行 `command`。 如果命令成功运行完毕,第一个返回值就是 **true**, 否则是 **nil** otherwise。 在第一个返回值之后,函数返回一个字符串加一个数字。如下: * **"`exit`":** 命令正常结束; 接下来的数字是命令的退出状态码。 * **"`signal`":** 命令被信号打断; 接下来的数字是打断该命令的信号。 如果不带参数调用, `os.execute` 在系统解释器存在的时候返回真。 ### `os.exit ([code [, close]])` 调用 ISO C 函数 `exit` 终止宿主程序。 如果 `code` 为 **true**, 返回的状态码是 `EXIT_SUCCESS`; 如果 `code` 为 **false**, 返回的状态码是 `EXIT_FAILURE`; 如果 `code` 是一个数字, 返回的状态码就是这个数字。 `code` 的默认值为 **true**。 如果第二个可选参数 `close` 为真, 在退出前关闭 Lua 状态机。 ### `os.getenv (varname)` 返回进程环境变量 `varname` 的值, 如果该变量未定义,返回 **nil** 。 ### `os.remove (filename)` 删除指定名字的文件(在 POSIX 系统上可以是一个空目录) 如果函数失败,返回 **nil** 加一个错误描述串及出错码。 ### `os.rename (oldname, newname)` 将名字为 `oldname` 的文件或目录更名为 `newname`。 如果函数失败,返回 **nil** 加一个错误描述串及出错码。 ### `os.setlocale (locale [, category])` 设置程序的当前区域。 `locale` 是一个区域设置的系统相关字符串; `category` 是一个描述有改变哪个分类的可选字符串: `"all"`,`"collate"`, `"ctype"`, `"monetary"`, `"numeric"`, 或 `"time"`; 默认的分类为 `"all"`。 此函数返回新区域的名字。 如果请求未被获准,返回 **nil** 。 当 `locale` 是一个空串, 当前区域被设置为一个在实现中定义好的本地区域。 当 `locale` 为字符串 "`C`", 当前区域被设置为标准 C 区域。 当第一个参数为 **nil** 时, 此函数仅返回当前区域指定分类的名字。 由于这个函数依赖 C 函数 `setlocale`, 它可能并非线程安全的。 ### `os.time ([table])` 当不传参数时,返回当前时刻。 如果传入一张表,就返回由这张表表示的时刻。 这张表必须包含域 `year`,`month`,及 `day`; 可以包含有 `hour` (默认为 12 ), `min` (默认为 0), `sec` (默认为 0),以及 `isdst` (默认为 **nil**)。 关于这些域的详细描述,参见 [`os.date`](#pdf-os.date) 函数。 返回值是一个含义由你的系统决定的数字。 在 POSIX,Windows,和其它一些系统中, 这个数字统计了从指定时间("epoch")开始经历的秒数。 对于另外的系统,其含义未定义, 你只能把 `time` 的返回数字用于 [`os.date`](#pdf-os.date) 和 [`os.difftime`](#pdf-os.difftime) 的参数。 ### `os.tmpname ()` 返回一个可用于临时文件的文件名字符串。 这个文件在使用前必须显式打开,不再使用时需要显式删除。 在 POSIX 系统上, 这个函数会以此文件名创建一个文件以回避安全风险。 (别人可能未经允许在获取到这个文件名到创建该文件之间的时刻创建此文件。) 你依旧需要在使用它的时候先打开,并最后删除(即使你没使用到)。 只有有可能,你更应该使用 [`io.tmpfile`](#pdf-io.tmpfile), 因为该文件可以在程序结束时自动删除。 ## 6.10 – 调试库 这个库提供了 Lua 程序调试接口([§4.9](#4.9))的功能。 其中一些函数违反了 Lua 代码的基本假定 (例如,不会从函数之外访问函数的局部变量; 用户数据的元表不会被 Lua 代码修改; Lua 程序不会崩溃), 因此它们有可能危害到其它代码的安全性。 此外,库里的一些函数可能运行的很慢。 这个库里的所有函数都提供在表 `debug` 内。 所有操作线程的函数,可选的第一个参数都是针对的线程。 默认值永远是当前线程。 ### `debug.debug ()` 进入一个用户交互模式,运行用户输入的每个字符串。 使用简单的命令以及其它调试设置,用户可以检阅全局变量和局部变量, 改变变量的值,计算一些表达式,等等。 输入一行仅包含 `cont` 的字符串将结束这个函数, 这样调用者就可以继续向下运行。 注意,`debug.debug` 输入的命令在文法上并没有内嵌到任何函数中, 因此不能直接去访问局部变量。 ### `debug.gethook ([thread])` 返回三个表示线程钩子设置的值: 当前钩子函数,当前钩子掩码,当前钩子计数 ([`debug.sethook`](#pdf-debug.sethook) 设置的那些)。 ### `debug.getinfo ([thread,] f [, what])` 返回关于一个函数信息的表。 你可以直接提供该函数, 也可以用一个数字 `f` 表示该函数。 数字 `f` 表示运行在指定线程的调用栈对应层次上的函数: 0 层表示当前函数(`getinfo` 自身); 1 层表示调用 `getinfo` 的函数 (除非是尾调用,这种情况不计入栈);等等。 如果 `f` 是一个比活动函数数量还大的数字, `getinfo` 返回 **nil**。 只有字符串 `what` 中有描述要填充哪些项, 返回的表可以包含 [`lua_getinfo`](#lua_getinfo) 能返回的所有项。 `what` 默认是返回提供的除合法行号表外的所有信息。 对于选项 '`f`' ,会在可能的情况下,增加 `func` 域保存函数自身。 对于选项 '`L`' ,会在可能的情况下,增加 `activelines` 域保存合法行号表。 例如,表达式 `debug.getinfo(1,"n")` 返回带有当前函数名字信息的表(如果找的到名字的话), 表达式 `debug.getinfo(print)` 返回关于 [`print`](#pdf-print) 函数的 包含有所有能提供信息的表。 ### `debug.getlocal ([thread,] f, local)` 此函数返回在栈的 `f` 层处函数的索引为 `local` 的局部变量 的名字和值。 这个函数不仅用于访问显式定义的局部变量,也包括形参、临时变量等。 第一个形参或是定义的第一个局部变量的索引为 1 , 然后遵循在代码中定义次序,以次类推。 其中只计算函数当前作用域的活动变量。 负索引指可变参数; -1 指第一个可变参数。 如果该索引处没有变量,函数返回 **nil**。 若指定的层次越界,抛出错误。 (你可以调用 [`debug.getinfo`](#pdf-debug.getinfo) 来检查层次是否合法。) 以 '`(`' (开括号)打头的变量名表示没有名字的变量 (比如是循环控制用到的控制变量, 或是去除了调试信息的代码块)。 参数 `f` 也可以是一个函数。 这种情况下,`getlocal` 仅返回函数形参的名字。 ### `debug.getmetatable (value)` 返回给定 `value` 的元表。 若其没有元表则返回 **nil** 。 ### `debug.getregistry ()` 返回注册表(参见 [§4.5](#4.5))。 ### `debug.getupvalue (f, up)` 此函数返回函数 `f` 的第 `up` 个上值的名字和值。 如果该函数没有那个上值,返回 **nil** 。 以 '`(`' (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)。 ### `debug.getuservalue (u)` 返回关联在 `u` 上的 Lua 值。 如果 `u` 并非用户数据,返回 **nil**。 ### `debug.sethook ([thread,] hook, mask [, count])` 将一个函数作为钩子函数设入。 字符串 `mask` 以及数字 `count` 决定了钩子将在何时调用。 掩码是由下列字符组合成的字符串,每个字符有其含义: * **'`c`':** 每当 Lua 调用一个函数时,调用钩子; * **'`r`':** 每当 Lua 从一个函数内返回时,调用钩子; * **'`l`':** 每当 Lua 进入新的一行时,调用钩子。 此外, 传入一个不为零的 `count` , 钩子将在每运行 `count` 条指令时调用。 如果不传入参数, [`debug.sethook`](#pdf-debug.sethook) 关闭钩子。 当钩子被调用时, 第一个参数是触发这次调用的事件: `"call"` (或 `"tail call"`), `"return"`, `"line"`, `"count"`。 对于行事件, 钩子的第二个参数是新的行号。 在钩子内,你可以调用 `getinfo` ,指定第 2 层, 来获得正在运行的函数的详细信息 (0 层指 `getinfo` 函数, 1 层指钩子函数)。 ### `debug.setlocal ([thread,] level, local, value)` 这个函数将 `value` 赋给 栈上第 `level` 层函数的第 `local` 个局部变量。 如果没有那个变量,函数返回 **nil** 。 如果 `level` 越界,抛出一个错误。 (你可以调用 [`debug.getinfo`](#pdf-debug.getinfo) 来检查层次是否合法。) 否则,它返回局部变量的名字。 关于变量索引和名字,参见 [`debug.getlocal`](#pdf-debug.getlocal)。 ### `debug.setmetatable (value, table)` 将 `value` 的元表设为 `table` (可以是 **nil**)。 返回 `value`。 ### `debug.setupvalue (f, up, value)` 这个函数将 `value` 设为函数 `f` 的第 `up` 个上值。 如果函数没有那个上值,返回 **nil** 否则,返回该上值的名字。 ### `debug.setuservalue (udata, value)` 将 `value` 设为 `udata` 的关联值。 `udata` 必须是一个完全用户数据。 返回 `udata`。 ### `debug.traceback ([thread,] [message [, level]])` 如果 `message` 有,且不是字符串或 **nil**, 函数不做任何处理直接返回 `message`。 否则,它返回调用栈的栈回溯信息。 字符串可选项 `message` 被添加在栈回溯信息的开头。 数字可选项 `level` 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 `traceback` 的那里)。 ### `debug.upvalueid (f, n)` 返回指定函数第 `n` 个上值的唯一标识符(一个轻量用户数据)。 这个唯一标识符可以让程序检查两个不同的闭包是否共享了上值。 若 Lua 闭包之间共享的是同一个上值 (即指向一个外部局部变量),会返回相同的标识符。 ### `debug.upvaluejoin (f1, n1, f2, n2)` 让 Lua 闭包 `f1` 的第 `n1` 个上值 引用 Lua 闭包 `f2` 的第 `n2` 个上值。