API/non-standard-lua-libs/zh: Difference between revisions

From OpenComputers: Rebooted
Jump to navigation Jump to search
imported>OCDoc Import
Imported from legacy OpenComputers documentation at ocdoc.cil.li
 
Clean up Markdown artifacts from DokuWiki migration
 
Line 1: Line 1:
= 非标准Lua库 =
= 非标准Lua库 =


大部分Lua标准库都是可用的,尽管其中的一些只是部分可用或是重新实现的——这意味着其行为可能会略有不同。请参阅[Lua 5.3 手册](http://www.lua.org/manual/5.3/manual.html#6)(英文)中关于标准库的文档。
大部分Lua标准库都是可用的,尽管其中的一些只是部分可用或是重新实现的——这意味着其行为可能会略有不同。请参阅[http://www.lua.org/manual/5.3/manual.html#6 Lua 5.3 手册](英文)中关于标准库的文档。


对“重新实现”的描述是基于OpenOS的,其他自定义的操作系统可能会有不同的细节。
对“重新实现”的描述是基于OpenOS的,其他自定义的操作系统可能会有不同的细节。
Line 11: Line 11:
基础库中的[原函数]([[http://www.lua.org/manual/5.3/manual.html#6.1)是可用的,但有以下不同|http://www.lua.org/manual/5.3/manual.html#6.1)是可用的,但有以下不同]]:
基础库中的[原函数]([[http://www.lua.org/manual/5.3/manual.html#6.1)是可用的,但有以下不同|http://www.lua.org/manual/5.3/manual.html#6.1)是可用的,但有以下不同]]:


* `collectgarbage`'''不可用'''. - `dofile`和`loadfile` 被重新实现为从已挂载的文件系统组件(使用[[API/filesystem/zh|文件系统 API]]或重新实现过的 `io` 库)内加载文件。 - `load`在默认情况下只能用来加载文本,不支持二进制或编译过的代码。注意,配置文件中可以启用字节码的加载,但非常不推荐这样做,因为这是一个'''重大安全风险'''。 - `print`、`io.write`、`io.stdout:write`和`term.write`都向stdout(标准输出)推送字符串,这在大多数情况下都指gpu渲染,即输出到屏幕上。
* <code>collectgarbage</code>'''不可用'''. - <code>dofile</code>和<code>loadfile</code> 被重新实现为从已挂载的文件系统组件(使用[[API/filesystem/zh|文件系统 API]]或重新实现过的 <code>io</code> 库)内加载文件。 - <code>load</code>在默认情况下只能用来加载文本,不支持二进制或编译过的代码。注意,配置文件中可以启用字节码的加载,但非常不推荐这样做,因为这是一个'''重大安全风险'''。 - <code>print</code>、<code>io.write</code>、<code>io.stdout:write</code>和<code>term.write</code>都向stdout(标准输出)推送字符串,这在大多数情况下都指gpu渲染,即输出到屏幕上。


== 协程操作 ==
== 协程操作 ==


`coroutine`库内的[原函数]([[http://www.lua.org/manual/5.3/manual.html#6.2)均可用,并没有可见的差异|http://www.lua.org/manual/5.3/manual.html#6.2)均可用,并没有可见的差异]]。
<code>coroutine</code>库内的[原函数]([[http://www.lua.org/manual/5.3/manual.html#6.2)均可用,并没有可见的差异|http://www.lua.org/manual/5.3/manual.html#6.2)均可用,并没有可见的差异]]。


注意,暴露给用户使用的`coroutine.resume`和`coroutine.yield`实现是封装过的,它们负责中止在一定时间(见配置文件)后还没有yield的代码,还可以区分系统产生的yield和用户产生的yield(系统会产生“bubble”,例如在关闭命令行或调用组件 API 时)。然而这不应该从用户代码层面被注意到。如果被注意到了,应该被认为是bug。
注意,暴露给用户使用的<code>coroutine.resume</code>和<code>coroutine.yield</code>实现是封装过的,它们负责中止在一定时间(见配置文件)后还没有yield的代码,还可以区分系统产生的yield和用户产生的yield(系统会产生“bubble”,例如在关闭命令行或调用组件 API 时)。然而这不应该从用户代码层面被注意到。如果被注意到了,应该被认为是bug。


== 模块 ==
== 模块 ==
Line 23: Line 23:
模块部分已针对OpenComputer重新实现。效果应该跟[原本的]([[http://www.lua.org/manual/5.3/manual.html#6.3)相同,但它没有以下函数|http://www.lua.org/manual/5.3/manual.html#6.3)相同,但它没有以下函数]]:
模块部分已针对OpenComputer重新实现。效果应该跟[原本的]([[http://www.lua.org/manual/5.3/manual.html#6.3)相同,但它没有以下函数|http://www.lua.org/manual/5.3/manual.html#6.3)相同,但它没有以下函数]]:


* `package.config`不存在且未使用。 - `package.cpath`不存在且未使用。 - `package.loadlib`未实现。
* <code>package.config</code>不存在且未使用。 - <code>package.cpath</code>不存在且未使用。 - <code>package.loadlib</code>未实现。


后两者不存在是因为OpenComputer不能加载C语言代码。
后两者不存在是因为OpenComputer不能加载C语言代码。


`require`是在全局范围内可用的方法,也就是说,你不需要加载任何模块就能访问它,你可以在脚本的第一行就使用它(这是很常见的用法)。它的工作依赖于`package`库,因此我们就在这里对它进行介绍。
<code>require</code>是在全局范围内可用的方法,也就是说,你不需要加载任何模块就能访问它,你可以在脚本的第一行就使用它(这是很常见的用法)。它的工作依赖于<code>package</code>库,因此我们就在这里对它进行介绍。


* <code>require(library: string): table</code>  
* <code>require(library: string): table</code>


<pre>返回给定名称对应的`library`。首先,如果该库先前已被加载过,那么`package`库已经缓存它了,`require`会返回该库的缓存版本。如果想要卸载已预缓存的库,参见`package.loaded`。如果库没有被缓存,就会搜索`package.path`直到找到匹配的库。 
<pre>返回给定名称对应的library。首先,如果该库先前已被加载过,那么package库已经缓存它了,require会返回该库的缓存版本。如果想要卸载已预缓存的库,参见package.loaded。如果库没有被缓存,就会搜索package.path直到找到匹配的库。
</pre>
</pre>


* <code>package.path</code> '''我们建议用户不要修改默认的`package.path`,相反地,用户应该把自定义库放入`/usr/lib/`中。'''
* <code>package.path</code> '''我们建议用户不要修改默认的<code>package.path</code>,相反地,用户应该把自定义库放入<code>/usr/lib/</code>中。'''


<pre>定义了一个供 `require` 遍历寻找库的搜索路径列表。它是一个用分号分隔的路径列表,使用`?`作为传递给`require`库名的占位符。下面的例子会使它更容易理解。 
<pre>定义了一个供 require 遍历寻找库的搜索路径列表。它是一个用分号分隔的路径列表,使用?作为传递给require库名的占位符。下面的例子会使它更容易理解。
</pre>
</pre>


默认 package.path: `/lib/?.lua;/usr/lib/?.lua;/home/lib/?.lua;./?.lua;/lib/?/init.lua;/usr/lib/?/init.lua;/home/lib/?/init.lua;./?/init.lua`
默认 package.path: <code>/lib/?.lua;/usr/lib/?.lua;/home/lib/?.lua;./?.lua;/lib/?/init.lua;/usr/lib/?/init.lua;/home/lib/?/init.lua;./?/init.lua</code>
如果用户尝试导入"foobar"
如果用户尝试导入"foobar"


<pre>`local foobar = require("foobar")` 
<pre>local foobar = require("foobar")
</pre>
</pre>


以下是`require`为了执行require("foobar") 而会去查找的文件的顺序。为了有趣,我们假设当前工作目录是 /tmp/。
以下是<code>require</code>为了执行require("foobar") 而会去查找的文件的顺序。为了有趣,我们假设当前工作目录是 /tmp/。


<pre>  - /lib/foobar.lua
<pre>  - /lib/foobar.lua
Line 59: Line 59:
* <code>package.loaded</code> '''修改package.loaded的行为并非标准操作'''
* <code>package.loaded</code> '''修改package.loaded的行为并非标准操作'''


<pre>一个包含已缓存库源码的表,键为库的名称(即传入 `require` 的那个),值是缓存的库本身。将该表中的某个值设为`nil`的效果为从缓存中删除这个库。为了操作系统的正常运行,有些库会假定保持加载状态。
<pre>一个包含已缓存库源码的表,键为库的名称(即传入 require 的那个),值是缓存的库本身。将该表中的某个值设为nil的效果为从缓存中删除这个库。为了操作系统的正常运行,有些库会假定保持加载状态。
</pre>
</pre>
== 字符串操作 ==
== 字符串操作 ==


`string`库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.4)均可用,没有任何变化|http://www.lua.org/manual/5.2/manual.html#6.4)均可用,没有任何变化]]。
<code>string</code>库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.4)均可用,没有任何变化|http://www.lua.org/manual/5.2/manual.html#6.4)均可用,没有任何变化]]。


注意,GPU API中的函数工作于UTF-8字符串环境下,进而`term.write`和`print`也一样。为了帮助你处理UTF-8字符串,我们准备了一个额外的库,即[[API/unicode/zh|Unicode API]]。
注意,GPU API中的函数工作于UTF-8字符串环境下,进而<code>term.write</code>和<code>print</code>也一样。为了帮助你处理UTF-8字符串,我们准备了一个额外的库,即[[API/unicode/zh|Unicode API]]。


== 表操作 ==
== 表操作 ==


`table`库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.5)均可用,没有任何变化|http://www.lua.org/manual/5.2/manual.html#6.5)均可用,没有任何变化]]。
<code>table</code>库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.5)均可用,没有任何变化|http://www.lua.org/manual/5.2/manual.html#6.5)均可用,没有任何变化]]。


== 数学函数 ==
== 数学函数 ==


`math` 库中的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.6)均可用,但有一点改动|http://www.lua.org/manual/5.2/manual.html#6.6)均可用,但有一点改动]]。
<code>math</code> 库中的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.6)均可用,但有一点改动|http://www.lua.org/manual/5.2/manual.html#6.6)均可用,但有一点改动]]。


* `math.random` 为每个 Lua state和计算机使用一个单独的 `java.util.Random` 实例。 - `math.randomseed` 也被应用于对应的实例。
* <code>math.random</code> 为每个 Lua state和计算机使用一个单独的 <code>java.util.Random</code> 实例。 - <code>math.randomseed</code> 也被应用于对应的实例。


== 位操作 ==
== 位操作 ==


`bit32`库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.7)均可用,没有任何变化|http://www.lua.org/manual/5.2/manual.html#6.7)均可用,没有任何变化]]。
<code>bit32</code>库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.7)均可用,没有任何变化|http://www.lua.org/manual/5.2/manual.html#6.7)均可用,没有任何变化]]。


== 输入输出功能 ==
== 输入输出功能 ==


`io`库中的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.8)都被很大程度上重新实现了,它们工作在已挂载的文件系统组件上。标准输入通过%60io.read|http://www.lua.org/manual/5.2/manual.html#6.8)都被很大程度上重新实现了,它们工作在已挂载的文件系统组件上。标准输入通过`io.read]]`(以及`term.read`和`io.stdin:read`)读取。 标准输出通过`io.write`(以及`term.write`、`io.stdout:write`和`print`)写入。标准错误通过`io.stderr:write`写入。
<code>io</code>库中的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.8)都被很大程度上重新实现了,它们工作在已挂载的文件系统组件上。标准输入通过%60io.read|http://www.lua.org/manual/5.2/manual.html#6.8)都被很大程度上重新实现了,它们工作在已挂载的文件系统组件上。标准输入通过<code>io.read]]</code>(以及<code>term.read</code>和<code>io.stdin:read</code>)读取。 标准输出通过<code>io.write</code>(以及<code>term.write</code>、<code>io.stdout:write</code>和<code>print</code>)写入。标准错误通过<code>io.stderr:write</code>写入。


大多数情况下,它们应该与标准Lua实现在'''功能上'''等价。也许它们返回的错误信息与原版Lua 不同,但大多数情况下原版Lua使用的也是基于平台的C异常库,因此将这些用于程序逻辑无论如何都不是一个好主意。
大多数情况下,它们应该与标准Lua实现在'''功能上'''等价。也许它们返回的错误信息与原版Lua 不同,但大多数情况下原版Lua使用的也是基于平台的C异常库,因此将这些用于程序逻辑无论如何都不是一个好主意。


'' `io.open(path, mode)`'''不支持'''`+`模式,即只支持`r`、`w`、`a`和`rb`(允许使用`wb` 和 `ab` 模式,但是二进制模式对写入来说没有意义)。 注意,`io.open()`返回[[API/buffer/zh#实例方法|带缓冲IO流]],有比较智能的API,如`read("''a")` 可以读取整个文件,或者`io.read("*l")`可以读取一行。 还需注意,带缓冲IO流(由 `io.open`返回的)可以是二进制模式或文本模式,而原始流(由 `filesystem.open`返回的)是且只能是二进制模式。 流是二进制模式还是文本模式只会影响到 `read`。
'' <code>io.open(path, mode)</code>'''不支持'''<code>+</code>模式,即只支持<code>r</code>、<code>w</code>、<code>a</code>和<code>rb</code>(允许使用<code>wb</code> 和 <code>ab</code> 模式,但是二进制模式对写入来说没有意义)。 注意,<code>io.open()</code>返回[[API/buffer/zh#实例方法|带缓冲IO流]],有比较智能的API,如<code>read("''a")</code> 可以读取整个文件,或者<code>io.read("*l")</code>可以读取一行。 还需注意,带缓冲IO流(由 <code>io.open</code>返回的)可以是二进制模式或文本模式,而原始流(由 <code>filesystem.open</code>返回的)是且只能是二进制模式。 流是二进制模式还是文本模式只会影响到 <code>read</code>。


<pre>`path` 是你想打开的文件的相对路径或绝对路径。
<pre>path 是你想打开的文件的相对路径或绝对路径。
</pre>
</pre>
<pre>`mode` 可以是 `nil` 或一个字符串, `nil` 默认为“r”。
<pre>mode 可以是 nil 或一个字符串, nil 默认为“r”。
</pre>
</pre>
# '''二进制模式'''  
= '''二进制模式''' =


<pre>`io.open(path, "rb")`或`filesystem.open(path)`返回的流是二进制格式。`filesystem.open(path, "rb")`的写法也可以,但从`filesystem.open`返回的流**永远**都是二进制模式。
<pre>io.open(path, "rb")或filesystem.open(path)返回的流是二进制格式。filesystem.open(path, "rb")的写法也可以,但从filesystem.open返回的流**永远**都是二进制模式。
二进制模式下的`stream:read(1)`读取一个字节。
二进制模式下的stream:read(1)读取一个字节。
通过`buffered_stream:read("*n")`读取一个数字值会将数据以单字节字符的形式读取。(缓冲流从 `io.open` 返回,并支持从流中解释数值)
通过buffered_stream:read("*n")读取一个数字值会将数据以单字节字符的形式读取。(缓冲流从 io.open 返回,并支持从流中解释数值)
</pre>
</pre>


* '''文本模式'''
* '''文本模式'''


<pre>只有从`io.open`返回的,模式中没有使用"b"的流才是文本模式。例如`io.open(path)`和`io.open(path, "r")`。`filesystem.open`返回的任何句柄都不是文本模式下的流。 
<pre>只有从io.open返回的,模式中没有使用"b"的流才是文本模式。例如io.open(path)和io.open(path, "r")。filesystem.open返回的任何句柄都不是文本模式下的流。
文本模式下的`stream:read(1)`读取单个Unicode字符。它可能是单字节的,或者甚至有3个字节,取决于文本。
文本模式下的stream:read(1)读取单个Unicode字符。它可能是单字节的,或者甚至有3个字节,取决于文本。
通过`buffered_stream:read("*n")`读取一个数字值会将数据以unicode字符的形式进行读取。(带缓冲IO流是由 `io,open` 返回的,支持从流中解释数值)
通过buffered_stream:read("*n")读取一个数字值会将数据以unicode字符的形式进行读取。(带缓冲IO流是由 io,open 返回的,支持从流中解释数值)
</pre>
</pre>


* `io.open(path, "r")` 相当于 `io.open(path)`,它以文本只读模式打开一个文件。
* <code>io.open(path, "r")</code> 相当于 <code>io.open(path)</code>,它以文本只读模式打开一个文件。
* `io.open(path, "rb")` 以二进制只读模式打开一个文件。
* <code>io.open(path, "rb")</code> 以二进制只读模式打开一个文件。
* `io.open(path, "w")` 删除文件的所有内容,以写模式打开文件。流是二进制模式还是文本模式并不影响写入。因此“w”在功能上等价于“wb”。
* <code>io.open(path, "w")</code> 删除文件的所有内容,以写模式打开文件。流是二进制模式还是文本模式并不影响写入。因此“w”在功能上等价于“wb”。
* `io.open(path, "a")` 以写模式打开一个文件并将文件句柄位置放在文件的末尾,这意味着下一次写入会追加到文件末尾。
* <code>io.open(path, "a")</code> 以写模式打开一个文件并将文件句柄位置放在文件的末尾,这意味着下一次写入会追加到文件末尾。
* `io.stdin`从模拟的stdin中读取数据,它默认为OpenOS shell中的用户输入。注意`io.read()`是`io.stdin:read()`的简写,它们会被解析为完全相同的操作。
* <code>io.stdin</code>从模拟的stdin中读取数据,它默认为OpenOS shell中的用户输入。注意<code>io.read()</code>是<code>io.stdin:read()</code>的简写,它们会被解析为完全相同的操作。


```lua io.stdin:read() -- 从stdin中读取 io.read() -- 也是从stdin中读取 term.read() -- 也是从stdin中读取 ```
<syntaxhighlight lang="lua">
io.stdin:read() -- 从stdin中读取 io.read() -- 也是从stdin中读取 term.read() -- 也是从stdin中读取
</syntaxhighlight>


* `io.stdout`将数据写到模拟的stdout,默认为在GPU上渲染。注意`io.write()`是`io.stdout:write()`的简称,它们会被解析为完全相同的操作。
* <code>io.stdout</code>将数据写到模拟的stdout,默认为在GPU上渲染。注意<code>io.write()</code>是<code>io.stdout:write()</code>的简称,它们会被解析为完全相同的操作。


```lua io.stdout:write("写入stdout") io.write("也是写入stdout") term.write("也是写入stdout,但如果字符串超过屏幕分辨率允许的宽度,就会自动换行") ```
<syntaxhighlight lang="lua">
io.stdout:write("写入stdout") io.write("也是写入stdout") term.write("也是写入stdout,但如果字符串超过屏幕分辨率允许的宽度,就会自动换行")
</syntaxhighlight>


* `io.stderr`将数据写到模拟的stderr,默认在GPU上渲染,但如果首选GPU和屏幕支持的话,会尝试用红色渲染。没有像stdin和stdout那样的使用stderr的快捷方式。
* <code>io.stderr</code>将数据写到模拟的stderr,默认在GPU上渲染,但如果首选GPU和屏幕支持的话,会尝试用红色渲染。没有像stdin和stdout那样的使用stderr的快捷方式。


```lua io.stderr:write("向stderr写入错误信息") ```
<syntaxhighlight lang="lua">
io.stderr:write("向stderr写入错误信息")
</syntaxhighlight>


== 操作系统功能 ==
== 操作系统功能 ==


`os`库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.9)已经被部分重新实现|http://www.lua.org/manual/5.2/manual.html#6.9)已经被部分重新实现]]。
<code>os</code>库内的[原函数]([[http://www.lua.org/manual/5.2/manual.html#6.9)已经被部分重新实现|http://www.lua.org/manual/5.2/manual.html#6.9)已经被部分重新实现]]。


* `os.clock`被重新实现为返回近似CPU时间,也就是计算机在一个执行器线程中实际运行的时间。这与计算机运行时间'''不一样''',获取此时间请参见[[API/computer/zh|computer.uptime]]。
* <code>os.clock</code>被重新实现为返回近似CPU时间,也就是计算机在一个执行器线程中实际运行的时间。这与计算机运行时间'''不一样''',获取此时间请参见[[API/computer/zh|computer.uptime]]。
* `os.date`被重新实现为使用游戏内时间,并支持大多数格式。
* <code>os.date</code>被重新实现为使用游戏内时间,并支持大多数格式。
* `os.execute`被重新实现,相当于从已挂载的文件系统中通过`shell.execute`启动程序。传入的字符串相当于在 shell 内输入的命令。
* <code>os.execute</code>被重新实现,相当于从已挂载的文件系统中通过<code>shell.execute</code>启动程序。传入的字符串相当于在 shell 内输入的命令。
* `os.exit`抛出一个错误并试图终止当前的协程。
* <code>os.exit</code>抛出一个错误并试图终止当前的协程。
* `os.setenv`添加Lua shell的环境变量。
* <code>os.setenv</code>添加Lua shell的环境变量。
* `os.remove`是`filesystem.remove`的别名。
* <code>os.remove</code>是<code>filesystem.remove</code>的别名。
* `os.rename`是`filesystem.rename`的别名。
* <code>os.rename</code>是<code>filesystem.rename</code>的别名。
* `os.setlocale`'''不可用'''。
* <code>os.setlocale</code>'''不可用'''。
* `os.time` 被重新实现为返回自世界创建以来的游戏时间。
* <code>os.time</code> 被重新实现为返回自世界创建以来的游戏时间。


<pre>注意这个时间是以“游戏内秒”为单位的。要获得世界创建起的tick数,需要将其乘以 `1000/60/60`(因为一天有24000个时间刻)然后减去6000。这个6000的偏移量不是随便设置的,它可以确保早上6点确实是早上6点。Minecraft莫名其妙地认为早上六点是零,可能是因为那是新游戏开始的时间…… 
<pre>注意这个时间是以“游戏内秒”为单位的。要获得世界创建起的tick数,需要将其乘以 1000/60/60(因为一天有24000个时间刻)然后减去6000。这个6000的偏移量不是随便设置的,它可以确保早上6点确实是早上6点。Minecraft莫名其妙地认为早上六点是零,可能是因为那是新游戏开始的时间……
</pre>
</pre>


* `os.tmpname` 已被重新实现,可以创建一个未使用的名字挂载到 `/tmp` 下。
* <code>os.tmpname</code> 已被重新实现,可以创建一个未使用的名字挂载到 <code>/tmp</code> 下。


额外增加了一个函数:
额外增加了一个函数:


* `os.sleep(seconds: number)`允许暂停脚本指定时间长度。`os.sleep`会消耗事件,但注册的处理函数和线程在休眠时仍会接收事件。换句话说,在睡眠状态下,信号仍然会被处理函数处理,也就是说你无法在睡眠结束后拉取睡眠期间堆积的信号,因为没有信号会留在队列中(或者说至少不是全部信号)。
* <code>os.sleep(seconds: number)</code>允许暂停脚本指定时间长度。<code>os.sleep</code>会消耗事件,但注册的处理函数和线程在休眠时仍会接收事件。换句话说,在睡眠状态下,信号仍然会被处理函数处理,也就是说你无法在睡眠结束后拉取睡眠期间堆积的信号,因为没有信号会留在队列中(或者说至少不是全部信号)。


此类别的一些新函数可以在[[API/computer/zh|computer(电脑) API]]中找到。
此类别的一些新函数可以在[[API/computer/zh|computer(电脑) API]]中找到。
Line 150: Line 156:
== 调试 ==
== 调试 ==


只实现了`debug.traceback`和`debug.getinfo`(1.5.9起),仅限被动信息。
只实现了<code>debug.traceback</code>和<code>debug.getinfo</code>(1.5.9起),仅限被动信息。


== 目录 ==
== 目录 ==


{{:API/contents/zh}}
{{:API/contents/zh}}

Latest revision as of 20:26, 24 August 2026

非标准Lua库

大部分Lua标准库都是可用的,尽管其中的一些只是部分可用或是重新实现的——这意味着其行为可能会略有不同。请参阅Lua 5.3 手册(英文)中关于标准库的文档。

对“重新实现”的描述是基于OpenOS的,其他自定义的操作系统可能会有不同的细节。

本页将尝试列出所有与标准库的差异。

基本函数

基础库中的[原函数]([[1]]:

  • collectgarbage不可用. - dofile和loadfile 被重新实现为从已挂载的文件系统组件(使用文件系统 API或重新实现过的 io 库)内加载文件。 - load在默认情况下只能用来加载文本,不支持二进制或编译过的代码。注意,配置文件中可以启用字节码的加载,但非常不推荐这样做,因为这是一个重大安全风险。 - print、io.write、io.stdout:write和term.write都向stdout(标准输出)推送字符串,这在大多数情况下都指gpu渲染,即输出到屏幕上。

协程操作

coroutine库内的[原函数]([[2]]。

注意,暴露给用户使用的coroutine.resume和coroutine.yield实现是封装过的,它们负责中止在一定时间(见配置文件)后还没有yield的代码,还可以区分系统产生的yield和用户产生的yield(系统会产生“bubble”,例如在关闭命令行或调用组件 API 时)。然而这不应该从用户代码层面被注意到。如果被注意到了,应该被认为是bug。

模块

模块部分已针对OpenComputer重新实现。效果应该跟[原本的]([[3]]:

  • package.config不存在且未使用。 - package.cpath不存在且未使用。 - package.loadlib未实现。

后两者不存在是因为OpenComputer不能加载C语言代码。

require是在全局范围内可用的方法,也就是说,你不需要加载任何模块就能访问它,你可以在脚本的第一行就使用它(这是很常见的用法)。它的工作依赖于package库,因此我们就在这里对它进行介绍。

  • require(library: string): table
返回给定名称对应的library。首先,如果该库先前已被加载过,那么package库已经缓存它了,require会返回该库的缓存版本。如果想要卸载已预缓存的库,参见package.loaded。如果库没有被缓存,就会搜索package.path直到找到匹配的库。
  • package.path 我们建议用户不要修改默认的package.path,相反地,用户应该把自定义库放入/usr/lib/中。
定义了一个供 require 遍历寻找库的搜索路径列表。它是一个用分号分隔的路径列表,使用?作为传递给require库名的占位符。下面的例子会使它更容易理解。

默认 package.path: /lib/?.lua;/usr/lib/?.lua;/home/lib/?.lua;./?.lua;/lib/?/init.lua;/usr/lib/?/init.lua;/home/lib/?/init.lua;./?/init.lua 如果用户尝试导入"foobar"

local foobar = require("foobar")

以下是require为了执行require("foobar") 而会去查找的文件的顺序。为了有趣,我们假设当前工作目录是 /tmp/。

  - /lib/foobar.lua
  * /usr/lib/foobar.lua
  * /home/lib/foobar.lua
  * /tmp/foobar.lua
  * /lib/foobar/init.lua
  * /usr/lib/foobar/init.lua
  * /home/lib/foobar/init.lua
  * /tmp/foobar/init.lua
  • package.loaded 修改package.loaded的行为并非标准操作
一个包含已缓存库源码的表,键为库的名称(即传入 require 的那个),值是缓存的库本身。将该表中的某个值设为nil的效果为从缓存中删除这个库。为了操作系统的正常运行,有些库会假定保持加载状态。

字符串操作

string库内的[原函数]([[4]]。

注意,GPU API中的函数工作于UTF-8字符串环境下,进而term.write和print也一样。为了帮助你处理UTF-8字符串,我们准备了一个额外的库,即Unicode API。

表操作

table库内的[原函数]([[5]]。

数学函数

math 库中的[原函数]([[6]]。

  • math.random 为每个 Lua state和计算机使用一个单独的 java.util.Random 实例。 - math.randomseed 也被应用于对应的实例。

位操作

bit32库内的[原函数]([[7]]。

输入输出功能

io库中的[原函数]([io.read](以及term.read和io.stdin:read)读取。 标准输出通过io.write(以及term.write、io.stdout:write和print)写入。标准错误通过io.stderr:write写入。

大多数情况下,它们应该与标准Lua实现在功能上等价。也许它们返回的错误信息与原版Lua 不同,但大多数情况下原版Lua使用的也是基于平台的C异常库,因此将这些用于程序逻辑无论如何都不是一个好主意。

io.open(path, mode)不支持+模式,即只支持r、w、a和rb(允许使用wb 和 ab 模式,但是二进制模式对写入来说没有意义)。 注意,io.open()返回带缓冲IO流,有比较智能的API,如read("a") 可以读取整个文件,或者io.read("*l")可以读取一行。 还需注意,带缓冲IO流(由 io.open返回的)可以是二进制模式或文本模式,而原始流(由 filesystem.open返回的)是且只能是二进制模式。 流是二进制模式还是文本模式只会影响到 read。

path 是你想打开的文件的相对路径或绝对路径。
mode 可以是 nil 或一个字符串, nil 默认为“r”。

二进制模式

io.open(path, "rb")或filesystem.open(path)返回的流是二进制格式。filesystem.open(path, "rb")的写法也可以,但从filesystem.open返回的流**永远**都是二进制模式。
二进制模式下的stream:read(1)读取一个字节。
通过buffered_stream:read("*n")读取一个数字值会将数据以单字节字符的形式读取。(缓冲流从 io.open 返回,并支持从流中解释数值)
  • 文本模式
只有从io.open返回的,模式中没有使用"b"的流才是文本模式。例如io.open(path)和io.open(path, "r")。filesystem.open返回的任何句柄都不是文本模式下的流。
文本模式下的stream:read(1)读取单个Unicode字符。它可能是单字节的,或者甚至有3个字节,取决于文本。
通过buffered_stream:read("*n")读取一个数字值会将数据以unicode字符的形式进行读取。(带缓冲IO流是由 io,open 返回的,支持从流中解释数值)
  • io.open(path, "r") 相当于 io.open(path),它以文本只读模式打开一个文件。
  • io.open(path, "rb") 以二进制只读模式打开一个文件。
  • io.open(path, "w") 删除文件的所有内容,以写模式打开文件。流是二进制模式还是文本模式并不影响写入。因此“w”在功能上等价于“wb”。
  • io.open(path, "a") 以写模式打开一个文件并将文件句柄位置放在文件的末尾,这意味着下一次写入会追加到文件末尾。
  • io.stdin从模拟的stdin中读取数据,它默认为OpenOS shell中的用户输入。注意io.read()是io.stdin:read()的简写,它们会被解析为完全相同的操作。
io.stdin:read() -- 从stdin中读取 io.read() -- 也是从stdin中读取 term.read() -- 也是从stdin中读取
  • io.stdout将数据写到模拟的stdout,默认为在GPU上渲染。注意io.write()是io.stdout:write()的简称,它们会被解析为完全相同的操作。
io.stdout:write("写入stdout") io.write("也是写入stdout") term.write("也是写入stdout,但如果字符串超过屏幕分辨率允许的宽度,就会自动换行")
  • io.stderr将数据写到模拟的stderr,默认在GPU上渲染,但如果首选GPU和屏幕支持的话,会尝试用红色渲染。没有像stdin和stdout那样的使用stderr的快捷方式。
io.stderr:write("向stderr写入错误信息")

操作系统功能

os库内的[原函数]([[8]]。

  • os.clock被重新实现为返回近似CPU时间,也就是计算机在一个执行器线程中实际运行的时间。这与计算机运行时间不一样,获取此时间请参见computer.uptime。
  • os.date被重新实现为使用游戏内时间,并支持大多数格式。
  • os.execute被重新实现,相当于从已挂载的文件系统中通过shell.execute启动程序。传入的字符串相当于在 shell 内输入的命令。
  • os.exit抛出一个错误并试图终止当前的协程。
  • os.setenv添加Lua shell的环境变量。
  • os.remove是filesystem.remove的别名。
  • os.rename是filesystem.rename的别名。
  • os.setlocale不可用。
  • os.time 被重新实现为返回自世界创建以来的游戏时间。
注意这个时间是以“游戏内秒”为单位的。要获得世界创建起的tick数,需要将其乘以 1000/60/60(因为一天有24000个时间刻)然后减去6000。这个6000的偏移量不是随便设置的,它可以确保早上6点确实是早上6点。Minecraft莫名其妙地认为早上六点是零,可能是因为那是新游戏开始的时间……
  • os.tmpname 已被重新实现,可以创建一个未使用的名字挂载到 /tmp 下。

额外增加了一个函数:

  • os.sleep(seconds: number)允许暂停脚本指定时间长度。os.sleep会消耗事件,但注册的处理函数和线程在休眠时仍会接收事件。换句话说,在睡眠状态下,信号仍然会被处理函数处理,也就是说你无法在睡眠结束后拉取睡眠期间堆积的信号,因为没有信号会留在队列中(或者说至少不是全部信号)。

此类别的一些新函数可以在computer(电脑) API中找到。

调试

只实现了debug.traceback和debug.getinfo(1.5.9起),仅限被动信息。

目录

< 100% 15% 15% >
API OpenOS buffer - colors - component - computer - event - filesystem - uuid - internet - keyboard - note - process - rc - robot - serialization - shell - sides - term - text - thread - transforms - unicode
::: Lua 库 coroutine - package - io - os