在搜索一个 Lua
文件时,函数 require
使用的路径与典型的路径略有不同。典型的路径是很多目录组成的列表,并且在其中搜索指定文件。不过, ISO C
( Lua
语言依赖的抽象平台)并没有目录的概念。所以,函数 require
使用的路径是一组模板,其中的每项都指定了将模块名(函数 require
的参数)转换为文件名的方式。更准确的说,这种路径中的每一个模板都是一个包含可选问号的文件名。对于每个模板,函数 require
会用模块名来替换每一个问号,然后检查结果是否存在对应的文件。如果不存在,则尝试下一个模板。路径中的模板以在大多数操作系统中很少被用于文件名的分号隔开。例如,考虑如下路径:
?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua
在使用这个路径时,调用 require "sql"
将尝试打开如下的 Lua
文件:
sql
sql.lua
c:\windows\sql
/usr/local/lua/sql/sql.lua
函数 require
只处理分号(作为分隔符)和问号,所有其他的部分(包括目录分隔符和文件扩展名)则由路径自己定义。
函数 require
用于搜索 Lua
文件的路径是变量 package.path
设置成环境变量 LUA_PATH_5_3
的值。如果这个环境变量没有被定义,那么 Lua
语言则尝试另一个环境变量 LUA_PATH
。如果这两个环境变量都没有被定义,那么 Lua
语言则使用一个编译时定义的默认路径。在使用一个环境变量的值时, Lua
语言会将其中所有的" ;;
"替换成默认路径。例如,如果将 LUA_PATH_5_3
设为" mydir/?.lua;;
",那么最终路径就会是模板" mydir/?.lua
"后跟默认路径。
搜索 C
标准库的路径的逻辑与此相同,只不过 C
标准库的路径来自变量 package.cpath
而不是 package.path
。类似地,这个变量的初始值也来自环境变量 LUA_CPATH_5_3
或 LUA_CPATH
。在 POSIX
系统中这个路径的典型值形如:
./?.so;/usr/local/lib/lua/5.3/?.so
请这一定义文件扩展名的路径。在上例中,所有模板使用的都是 .so
,而在 Windows
操作系统中此典型路径通常形如:
.\?.dll;C:\Program Files\Lua503\dll\?.dll
函数 package.searchpath
中实现了搜索库的所有规则,该函数的参数包括模块名和路径,然后遵循上述规则来搜索文件。函数 package.searchpath
要么返回第一个存在的文件的文件名,要么返回 nil
外加描述所有文件都无法成功打开的错误信息,如下:
> path = ".\\?.dll;C:Program Files\\Lua503\\dll\\?.dll" > print(package.searchpath("X", path)) nil no file '.\X.dll' no file 'c:\Program Files\Lua503\dll\X.dll'
作为一个有趣的练习,我们来实现一个与函数 package.searchpath
类似的函数
function search (modname, path) modname = string.gsub(modname, "%.", "/") local msg = {} for c in string.gmatch(path, "[^;]+") do local fname = string.gsub(c, "?", modname) local f = io.open(fname) if f then f:close() return fname else msg[#msg + 1] = string.format("\n\tno file '%s'", fname); end end return nil, table.concat(msg) -- 没找到 end
上述函数首先替换目录分隔符,在本例中即把所有的点换成斜杠。之后,该函数遍历路径中的所有组成部分,也就是每一个不含分号的最长匹配。对于每一个组成部分,该函数使用模块名来替换问号得到最终的文件名,然后检查相应的文件是否存在。如果存在,该函数关闭这个文件,然后返回文件的名称;否则,该函数保存失败的文件名用于可能的错误信息(注意字符串缓冲区在避免创建无用的长字符串时的作用)。如果一个文件都找不到,该函数返回 nil
及最终的错误信息。