- 0X31 模块编写
当调用require时,其实Lua要分好多步骤在多个地方查找有没有同名模块.在一个地方找不到就去下一个地方寻找.package.searchers这张表中定义了这些步骤和步骤的执行顺序.现在按其中函数的顺序索引依次介绍:
- 第一个函数最先执行,它在package.preload表中搜寻.
- 第二个函数在指定路径中搜寻Lua的脚本文件并加载这些模块.
- 第三个函数在指定的路径中搜寻C语言编译后的库文件并加载这种模块.关于Lua和C语言的交互我们将在后边进行讨论.
- 第四个函数也和C库的加载有关.
require在搜寻模块脚本文件的时候是在一些指定的路径之下搜寻的.这些路径保存在字符串变量package.path中,如果你的模块文件所在路径恰好不在这些路径这下可以,通过追加package.path的方法把自己的模块脚本所在路径添加进去.
例如我希望把"/home/myname"文件夹下的所有后缀名为"luna"的文件识别为Lua的模块脚本就执行:
package.path=package.path..';/home/myname/?.luna'
这里有几个符号的约定:
- "/"用于表示目录层次关系.在Windows系统中这个符号会变为"".
- ";"用于分割两个目录.package.path是一个整个字符串,但是其中包含多个路径.不同的路径中用";"进行分隔.这里的演示代码是追加path变量(而不是彻底重置),所以需要把我们追加的路径前加一个分号,用于和原来的路径分隔开.
- "?"路径中的问号是个占位符.在实际搜索中它会被require的参数替换.例如我们调用
require('mymod')
时实际在找"/home/myname/mymod.luna"等文件.
以上符号都是在字符串package.config中设置的.在这个变量中还额外设置了一个用于Windows环境中的特殊符号"!"和一个用于和C语言互动的符号"-".(和C相关的内容以再介绍.)
Lua有一个函数用于搜索在特定路径下的文件.由于它被打包为package.searchpath,所以它很可能它是实现模块搜索的基础,require函数在内部调用了它.
其函数原型为package.searchpath (name, path [, sep [, rep]]).其中的name和path是调用时必须给出的参数.sep和rep是调用时可以省略的参数.这四个参数都是字符串.
name和path都是字符串,这个函数会在path表示的路径中搜索名为name的文件.
如果找到则返以字符串回第一个复合要求的文件的完整路径(包含完整文件名).如果失败则返回两个值,第一个是nil,第二个是错误信息.
如果要同时搜索若干个路径,则把这些路径用分号隔开写在同一个字符串中作为path传入.
sep的默认值为'.',rep的默认值为'/'.
path中所有的问号'?'会被name替换.而如果name中含有sep,则所有name中的sep会先被换成rep.
关于路径替换,结合的这些语法近似于正则表达式.有兴趣可以在后续课程中学到正则表达式相关的内容.
仅仅是模块加载的功能,还算不上稀奇.从C语言,C++,Java,C#到XML,HTML.几乎所有的语言都有类似模块调用的机制.Lua等脚本语言相对应传统的编译性语言的一大特色就是它可以在运行中把符合自身语法规则的字符串当作脚本(或者模块)执行.
又由于Lua程序在运行时根据需要构造各种字符串.这种动态执行脚本的能力,相当于让Lua程序可以很轻松地自动生成程序甚至是复制和修改自身.
要使用这些功能我们还需要两方面的基础--输出输出和文件读写,Lua的编译运行原理.
后边我们将对此做简要介绍.
除了可以把已经写好的Lua封装成模块,还能把Lua不能实现或者难以实现的功能封装成模块供Lua调用.例如Lua没有提供多少对硬件的直接控制功能.就可以先用C写好相关功能然后封装成Lua的模块.再比如,获取当前时间这类看似无法在Lua内部解决,而是需要系统支持的功能,也可以封装成Lua的模块供Lua调用.
关于C和Lua之间的相互调用,现在还到介绍的时候.但是我们可以先来看看Lua标准库提供了哪些可供我们调用的工具模块.
- 0X34 获取系统时间
- 0X35 随机数
- 0X37 搜寻字符串
- 0X3B 命令行参数
- 0X3D 字符串压缩