Lua模块与包
概述
模块化编程是现代软件开发的重要实践。Lua提供了强大的模块系统,允许开发者将代码组织成可重用的模块,并通过包管理机制来组织和分发这些模块。
1. 模块基础概念
1.1 什么是模块
模块是一个包含函数、变量和其他Lua对象的表。模块的主要目的是:
- 代码重用 - 避免重复编写相同的代码
- 命名空间 - 避免全局变量污染
- 封装 - 隐藏内部实现细节
- 组织 - 将相关功能组织在一起
lua
-- 简单的模块示例
local math_utils = {}
-- 公有函数
function math_utils.add(a, b)
return a + b
end
function math_utils.multiply(a, b)
return a * b
end
-- 私有函数(局部函数)
local function validate_number(n)
return type(n) == "number"
end
-- 使用私有函数的公有函数
function math_utils.safe_divide(a, b)
if not validate_number(a) or not validate_number(b) then
error("参数必须是数字")
end
if b == 0 then
error("除数不能为零")
end
return a / b
end
-- 模块常量
math_utils.PI = 3.14159265359
math_utils.E = 2.71828182846
return math_utils1.2 模块的创建方式
lua
-- 方式1: 直接返回表
local string_utils = {}
function string_utils.trim(str)
return string.match(str, "^%s*(.-)%s*$")
end
function string_utils.split(str, delimiter)
local result = {}
local pattern = "([^" .. delimiter .. "]+)"
for match in string.gmatch(str, pattern) do
table.insert(result, match)
end
return result
end
return string_utils
-- 方式2: 使用setmetatable创建模块
local array_utils = {}
array_utils.__index = array_utils
function array_utils.new(initial_data)
local instance = initial_data or {}
return setmetatable(instance, array_utils)
end
function array_utils:push(value)
table.insert(self, value)
return self
end
function array_utils:pop()
return table.remove(self)
end
function array_utils:length()
return #self
end
return array_utils
-- 方式3: 函数式模块
local function create_counter(initial_value)
local count = initial_value or 0
return {
increment = function()
count = count + 1
return count
end,
decrement = function()
count = count - 1
return count
end,
get = function()
return count
end,
reset = function()
count = initial_value or 0
return count
end
}
end
return create_counter2. require函数和模块加载
2.1 require函数的使用
lua
-- 加载模块的基本方式
local math_utils = require("math_utils")
local result = math_utils.add(10, 20)
print(result) -- 30
-- require的特性
-- 1. 模块只会被加载一次,后续require返回缓存的结果
local utils1 = require("math_utils")
local utils2 = require("math_utils")
print(utils1 == utils2) -- true,是同一个对象
-- 2. require会在package.path中搜索模块文件
print("搜索路径:", package.path)
-- 3. 可以加载不同类型的模块
local json = require("json") -- Lua模块
local socket = require("socket") -- C模块(如果安装了)
-- 模块加载示例
local function demonstrate_module_loading()
print("=== 模块加载演示 ===")
-- 创建一个简单的模块文件内容
local module_content = [[
-- simple_module.lua
local M = {}
M.version = "1.0.0"
M.name = "SimpleModule"
function M.greet(name)
return "Hello, " .. (name or "World") .. "!"
end
function M.get_info()
return {
name = M.name,
version = M.version,
loaded_at = os.date()
}
end
print("SimpleModule loaded at " .. os.date())
return M
]]
-- 写入模块文件
local file = io.open("simple_module.lua", "w")
file:write(module_content)
file:close()
-- 加载模块
local simple = require("simple_module")
print("模块名称:", simple.name)
print("模块版本:", simple.version)
print(simple.greet("Lua"))
local info = simple.get_info()
for k, v in pairs(info) do
print(k .. ":", v)
end
-- 清理
os.remove("simple_module.lua")
end
demonstrate_module_loading()2.2 模块搜索路径
lua
-- 模块搜索路径管理
local function module_path_management()
print("\n=== 模块搜索路径 ===")
-- 查看当前搜索路径
print("Lua模块搜索路径:")
for path in string.gmatch(package.path, "([^;]+)") do
print(" " .. path)
end
print("\nC模块搜索路径:")
for path in string.gmatch(package.cpath, "([^;]+)") do
print(" " .. path)
end
-- 添加自定义搜索路径
local function add_module_path(path)
package.path = package.path .. ";" .. path .. "/?.lua"
package.path = package.path .. ";" .. path .. "/?/init.lua"
end
-- 示例:添加自定义路径
add_module_path("./my_modules")
add_module_path("./lib")
print("\n添加自定义路径后:")
local paths = {}
for path in string.gmatch(package.path, "([^;]+)") do
table.insert(paths, path)
end
-- 只显示新添加的路径
for i = #paths - 3, #paths do
if paths[i] then
print(" " .. paths[i])
end
end
end
module_path_management()2.3 模块缓存机制
lua
-- 模块缓存机制
local function module_cache_management()
print("\n=== 模块缓存机制 ===")
-- 查看已加载的模块
print("已加载的模块:")
for name, module in pairs(package.loaded) do
if type(name) == "string" and not string.match(name, "^_") then
print(" " .. name .. ": " .. type(module))
end
end
-- 创建测试模块
local test_module_content = [[
local M = {}
M.load_time = os.time()
M.load_count = (M.load_count or 0) + 1
print("Test module loaded, count: " .. M.load_count)
return M
]]
local file = io.open("test_cache.lua", "w")
file:write(test_module_content)
file:close()
-- 第一次加载
print("\n第一次加载:")
local mod1 = require("test_cache")
print("加载时间:", mod1.load_time)
-- 第二次加载(从缓存)
print("\n第二次加载:")
local mod2 = require("test_cache")
print("是否为同一对象:", mod1 == mod2)
print("加载时间:", mod2.load_time)
-- 强制重新加载模块
print("\n强制重新加载:")
package.loaded["test_cache"] = nil -- 清除缓存
local mod3 = require("test_cache")
print("是否为同一对象:", mod1 == mod3)
print("新的加载时间:", mod3.load_time)
-- 清理
os.remove("test_cache.lua")
package.loaded["test_cache"] = nil
end
module_cache_management()3. 高级模块模式
3.1 单例模式
lua
-- 单例模式模块
local function create_singleton_module()
local singleton_content = [[
-- singleton.lua
local M = {}
local instance = nil
local function create_instance()
return {
id = math.random(1000000),
created_at = os.time(),
data = {}
}
end
function M.get_instance()
if not instance then
instance = create_instance()
print("创建新的单例实例,ID:", instance.id)
end
return instance
end
function M.reset()
instance = nil
print("单例实例已重置")
end
return M
]]
local file = io.open("singleton.lua", "w")
file:write(singleton_content)
file:close()
-- 测试单例模式
print("\n=== 单例模式测试 ===")
local singleton = require("singleton")
local inst1 = singleton.get_instance()
local inst2 = singleton.get_instance()
print("实例1 ID:", inst1.id)
print("实例2 ID:", inst2.id)
print("是否为同一实例:", inst1 == inst2)
-- 重置并获取新实例
singleton.reset()
local inst3 = singleton.get_instance()
print("重置后实例3 ID:", inst3.id)
print("与实例1是否相同:", inst1 == inst3)
-- 清理
os.remove("singleton.lua")
package.loaded["singleton"] = nil
end
create_singleton_module()3.2 工厂模式
lua
-- 工厂模式模块
local function create_factory_module()
local factory_content = [[
-- shape_factory.lua
local M = {}
-- 形状类定义
local Shape = {}
Shape.__index = Shape
function Shape:new(type_name)
local obj = {type = type_name}
return setmetatable(obj, self)
end
function Shape:area()
error("子类必须实现area方法")
end
function Shape:perimeter()
error("子类必须实现perimeter方法")
end
-- 矩形类
local Rectangle = setmetatable({}, Shape)
Rectangle.__index = Rectangle
function Rectangle:new(width, height)
local obj = Shape.new(self, "Rectangle")
obj.width = width
obj.height = height
return obj
end
function Rectangle:area()
return self.width * self.height
end
function Rectangle:perimeter()
return 2 * (self.width + self.height)
end
-- 圆形类
local Circle = setmetatable({}, Shape)
Circle.__index = Circle
function Circle:new(radius)
local obj = Shape.new(self, "Circle")
obj.radius = radius
return obj
end
function Circle:area()
return math.pi * self.radius * self.radius
end
function Circle:perimeter()
return 2 * math.pi * self.radius
end
-- 工厂方法
function M.create_shape(shape_type, ...)
if shape_type == "rectangle" then
return Rectangle:new(...)
elseif shape_type == "circle" then
return Circle:new(...)
else
error("未知的形状类型: " .. shape_type)
end
end
function M.get_supported_shapes()
return {"rectangle", "circle"}
end
return M
]]
local file = io.open("shape_factory.lua", "w")
file:write(factory_content)
file:close()
-- 测试工厂模式
print("\n=== 工厂模式测试 ===")
local factory = require("shape_factory")
-- 创建不同的形状
local rect = factory.create_shape("rectangle", 10, 5)
local circle = factory.create_shape("circle", 3)
print("矩形面积:", rect:area())
print("矩形周长:", rect:perimeter())
print("圆形面积:", string.format("%.2f", circle:area()))
print("圆形周长:", string.format("%.2f", circle:perimeter()))
print("支持的形状:", table.concat(factory.get_supported_shapes(), ", "))
-- 清理
os.remove("shape_factory.lua")
package.loaded["shape_factory"] = nil
end
create_factory_module()3.3 观察者模式
lua
-- 观察者模式模块
local function create_observer_module()
local observer_content = [[
-- event_emitter.lua
local M = {}
function M.new()
local emitter = {
listeners = {}
}
function emitter:on(event, callback)
if not self.listeners[event] then
self.listeners[event] = {}
end
table.insert(self.listeners[event], callback)
return self
end
function emitter:off(event, callback)
if not self.listeners[event] then
return self
end
for i, listener in ipairs(self.listeners[event]) do
if listener == callback then
table.remove(self.listeners[event], i)
break
end
end
return self
end
function emitter:emit(event, ...)
if not self.listeners[event] then
return self
end
for _, callback in ipairs(self.listeners[event]) do
local success, err = pcall(callback, ...)
if not success then
print("事件处理器错误:", err)
end
end
return self
end
function emitter:once(event, callback)
local function wrapper(...)
callback(...)
self:off(event, wrapper)
end
return self:on(event, wrapper)
end
function emitter:listener_count(event)
return self.listeners[event] and #self.listeners[event] or 0
end
return emitter
end
return M
]]
local file = io.open("event_emitter.lua", "w")
file:write(observer_content)
file:close()
-- 测试观察者模式
print("\n=== 观察者模式测试 ===")
local EventEmitter = require("event_emitter")
local emitter = EventEmitter.new()
-- 添加事件监听器
emitter:on("user_login", function(username)
print("用户登录:", username)
end)
emitter:on("user_login", function(username)
print("记录日志: 用户", username, "在", os.date(), "登录")
end)
emitter:once("user_logout", function(username)
print("用户", username, "退出登录(只触发一次)")
end)
-- 触发事件
emitter:emit("user_login", "Alice")
emitter:emit("user_logout", "Alice")
emitter:emit("user_logout", "Alice") -- 第二次不会触发
print("user_login事件监听器数量:", emitter:listener_count("user_login"))
print("user_logout事件监听器数量:", emitter:listener_count("user_logout"))
-- 清理
os.remove("event_emitter.lua")
package.loaded["event_emitter"] = nil
end
create_observer_module()4. 包管理
4.1 包的组织结构
lua
-- 包的组织结构示例
local function create_package_structure()
print("\n=== 包结构示例 ===")
-- 创建包目录结构
local function create_directory(path)
os.execute("mkdir " .. path .. " 2>nul") -- Windows
os.execute("mkdir -p " .. path .. " 2>/dev/null") -- Unix
end
-- 创建目录
create_directory("mypackage")
create_directory("mypackage/utils")
create_directory("mypackage/data")
-- 主模块文件
local main_module = [[
-- mypackage/init.lua
local M = {}
-- 加载子模块
M.utils = require("mypackage.utils")
M.data = require("mypackage.data")
M.version = "1.0.0"
M.name = "MyPackage"
function M.info()
return {
name = M.name,
version = M.version,
modules = {"utils", "data"}
}
end
return M
]]
-- 工具模块
local utils_module = [[
-- mypackage/utils/init.lua
local M = {}
M.string = require("mypackage.utils.string")
M.array = require("mypackage.utils.array")
return M
]]
-- 字符串工具
local string_utils = [[
-- mypackage/utils/string.lua
local M = {}
function M.trim(str)
return string.match(str, "^%s*(.-)%s*$")
end
function M.capitalize(str)
return string.upper(string.sub(str, 1, 1)) .. string.lower(string.sub(str, 2))
end
function M.reverse(str)
return string.reverse(str)
end
return M
]]
-- 数组工具
local array_utils = [[
-- mypackage/utils/array.lua
local M = {}
function M.map(array, func)
local result = {}
for i, v in ipairs(array) do
result[i] = func(v)
end
return result
end
function M.filter(array, predicate)
local result = {}
for _, v in ipairs(array) do
if predicate(v) then
table.insert(result, v)
end
end
return result
end
function M.reduce(array, func, initial)
local result = initial
for _, v in ipairs(array) do
result = func(result, v)
end
return result
end
return M
]]
-- 数据模块
local data_module = [[
-- mypackage/data/init.lua
local M = {}
M.constants = {
PI = 3.14159265359,
E = 2.71828182846,
GOLDEN_RATIO = 1.61803398875
}
M.config = {
debug = false,
max_retries = 3,
timeout = 30
}
function M.get_constant(name)
return M.constants[name]
end
function M.get_config(key)
return M.config[key]
end
function M.set_config(key, value)
M.config[key] = value
end
return M
]]
-- 写入文件
local files = {
["mypackage/init.lua"] = main_module,
["mypackage/utils/init.lua"] = utils_module,
["mypackage/utils/string.lua"] = string_utils,
["mypackage/utils/array.lua"] = array_utils,
["mypackage/data/init.lua"] = data_module
}
for filename, content in pairs(files) do
local file = io.open(filename, "w")
if file then
file:write(content)
file:close()
print("创建文件:", filename)
end
end
-- 测试包的使用
print("\n测试包的使用:")
local mypackage = require("mypackage")
print("包信息:")
local info = mypackage.info()
for k, v in pairs(info) do
if type(v) == "table" then
print(" " .. k .. ":", table.concat(v, ", "))
else
print(" " .. k .. ":", v)
end
end
-- 使用字符串工具
local str = " hello world "
print("\n字符串工具测试:")
print("原始:", "'" .. str .. "'")
print("trim:", "'" .. mypackage.utils.string.trim(str) .. "'")
print("capitalize:", mypackage.utils.string.capitalize(str))
print("reverse:", mypackage.utils.string.reverse("hello"))
-- 使用数组工具
local numbers = {1, 2, 3, 4, 5}
print("\n数组工具测试:")
print("原数组:", table.concat(numbers, ", "))
local doubled = mypackage.utils.array.map(numbers, function(x) return x * 2 end)
print("翻倍:", table.concat(doubled, ", "))
local evens = mypackage.utils.array.filter(numbers, function(x) return x % 2 == 0 end)
print("偶数:", table.concat(evens, ", "))
local sum = mypackage.utils.array.reduce(numbers, function(acc, x) return acc + x end, 0)
print("求和:", sum)
-- 使用数据模块
print("\n数据模块测试:")
print("PI:", mypackage.data.get_constant("PI"))
print("调试模式:", mypackage.data.get_config("debug"))
mypackage.data.set_config("debug", true)
print("设置后调试模式:", mypackage.data.get_config("debug"))
end
create_package_structure()4.2 版本管理
lua
-- 版本管理示例
local function version_management_example()
print("\n=== 版本管理示例 ===")
local version_module = [[
-- version_manager.lua
local M = {}
-- 版本比较函数
function M.parse_version(version_string)
local major, minor, patch = string.match(version_string, "(%d+)%.(%d+)%.(%d+)")
if not major then
error("无效的版本格式: " .. version_string)
end
return {
major = tonumber(major),
minor = tonumber(minor),
patch = tonumber(patch),
string = version_string
}
end
function M.compare_versions(v1, v2)
local version1 = type(v1) == "string" and M.parse_version(v1) or v1
local version2 = type(v2) == "string" and M.parse_version(v2) or v2
if version1.major ~= version2.major then
return version1.major - version2.major
end
if version1.minor ~= version2.minor then
return version1.minor - version2.minor
end
return version1.patch - version2.patch
end
function M.is_compatible(required_version, current_version)
local req = M.parse_version(required_version)
local cur = M.parse_version(current_version)
-- 主版本号必须相同,次版本号和补丁版本号可以更高
return cur.major == req.major and
(cur.minor > req.minor or
(cur.minor == req.minor and cur.patch >= req.patch))
end
-- 模块注册表
M.registry = {}
function M.register_module(name, version, module_obj)
if not M.registry[name] then
M.registry[name] = {}
end
M.registry[name][version] = {
version = M.parse_version(version),
module = module_obj,
registered_at = os.time()
}
end
function M.require_version(name, required_version)
if not M.registry[name] then
error("模块未注册: " .. name)
end
-- 查找兼容的版本
local best_version = nil
local best_info = nil
for version_string, info in pairs(M.registry[name]) do
if M.is_compatible(required_version, version_string) then
if not best_version or M.compare_versions(version_string, best_version) > 0 then
best_version = version_string
best_info = info
end
end
end
if not best_info then
error(string.format("找不到兼容版本的模块 %s (需要: %s)", name, required_version))
end
return best_info.module, best_version
end
function M.list_modules()
local result = {}
for name, versions in pairs(M.registry) do
local version_list = {}
for version in pairs(versions) do
table.insert(version_list, version)
end
table.sort(version_list, function(a, b)
return M.compare_versions(a, b) > 0
end)
result[name] = version_list
end
return result
end
return M
]]
local file = io.open("version_manager.lua", "w")
file:write(version_module)
file:close()
-- 测试版本管理
local vm = require("version_manager")
-- 注册不同版本的模块
vm.register_module("utils", "1.0.0", {name = "Utils v1.0.0"})
vm.register_module("utils", "1.1.0", {name = "Utils v1.1.0", new_feature = true})
vm.register_module("utils", "2.0.0", {name = "Utils v2.0.0", breaking_change = true})
vm.register_module("http", "0.9.0", {name = "HTTP v0.9.0"})
vm.register_module("http", "1.0.0", {name = "HTTP v1.0.0"})
-- 测试版本比较
print("版本比较测试:")
print("1.0.0 vs 1.1.0:", vm.compare_versions("1.0.0", "1.1.0"))
print("2.0.0 vs 1.9.9:", vm.compare_versions("2.0.0", "1.9.9"))
print("1.5.0 vs 1.5.0:", vm.compare_versions("1.5.0", "1.5.0"))
-- 测试兼容性
print("\n兼容性测试:")
print("需要1.0.0,当前1.1.0:", vm.is_compatible("1.0.0", "1.1.0"))
print("需要1.1.0,当前1.0.0:", vm.is_compatible("1.1.0", "1.0.0"))
print("需要1.0.0,当前2.0.0:", vm.is_compatible("1.0.0", "2.0.0"))
-- 测试版本要求
print("\n版本要求测试:")
local utils, version = vm.require_version("utils", "1.0.0")
print("需要utils 1.0.0,获得:", utils.name, "版本:", version)
local http, version = vm.require_version("http", "0.5.0")
print("需要http 0.5.0,获得:", http.name, "版本:", version)
-- 列出所有模块
print("\n已注册的模块:")
local modules = vm.list_modules()
for name, versions in pairs(modules) do
print(" " .. name .. ": " .. table.concat(versions, ", "))
end
-- 清理
os.remove("version_manager.lua")
package.loaded["version_manager"] = nil
end
version_management_example()5. 实际应用示例
5.1 配置管理包
lua
-- 配置管理包示例
local function create_config_package()
print("\n=== 配置管理包 ===")
local config_package = [[
-- config_manager.lua
local M = {}
-- 默认配置
local default_config = {
app = {
name = "MyApp",
version = "1.0.0",
debug = false
},
database = {
host = "localhost",
port = 3306,
timeout = 30
},
server = {
port = 8080,
max_connections = 100
}
}
-- 当前配置
local current_config = {}
-- 深度复制函数
local function deep_copy(obj)
if type(obj) ~= "table" then
return obj
end
local copy = {}
for k, v in pairs(obj) do
copy[k] = deep_copy(v)
end
return copy
end
-- 深度合并函数
local function deep_merge(target, source)
for k, v in pairs(source) do
if type(v) == "table" and type(target[k]) == "table" then
deep_merge(target[k], v)
else
target[k] = v
end
end
end
-- 初始化配置
function M.init(user_config)
current_config = deep_copy(default_config)
if user_config then
deep_merge(current_config, user_config)
end
end
-- 获取配置值
function M.get(path, default_value)
local keys = {}
for key in string.gmatch(path, "([^%.]+)") do
table.insert(keys, key)
end
local current = current_config
for _, key in ipairs(keys) do
if type(current) ~= "table" or current[key] == nil then
return default_value
end
current = current[key]
end
return current
end
-- 设置配置值
function M.set(path, value)
local keys = {}
for key in string.gmatch(path, "([^%.]+)") do
table.insert(keys, key)
end
local current = current_config
for i = 1, #keys - 1 do
local key = keys[i]
if type(current[key]) ~= "table" then
current[key] = {}
end
current = current[key]
end
current[keys[#keys]] = value
end
-- 获取所有配置
function M.get_all()
return deep_copy(current_config)
end
-- 重置为默认配置
function M.reset()
current_config = deep_copy(default_config)
end
-- 从文件加载配置
function M.load_from_file(filename)
local file = io.open(filename, "r")
if not file then
return false, "无法打开配置文件: " .. filename
end
local content = file:read("*a")
file:close()
-- 简单的JSON解析(仅支持基本格式)
local success, config = pcall(load("return " .. content))
if not success then
return false, "配置文件格式错误"
end
deep_merge(current_config, config)
return true
end
-- 保存配置到文件
function M.save_to_file(filename)
local function serialize(obj, indent)
indent = indent or 0
local spaces = string.rep(" ", indent)
if type(obj) == "table" then
local result = "{\n"
for k, v in pairs(obj) do
local key = type(k) == "string" and string.format('"%s"', k) or tostring(k)
result = result .. spaces .. " " .. key .. " = " .. serialize(v, indent + 1) .. ",\n"
end
result = result .. spaces .. "}"
return result
elseif type(obj) == "string" then
return string.format('"%s"', obj)
else
return tostring(obj)
end
end
local file = io.open(filename, "w")
if not file then
return false, "无法创建配置文件: " .. filename
end
file:write(serialize(current_config))
file:close()
return true
end
-- 验证配置
function M.validate(schema)
local function validate_value(value, rule)
if rule.type and type(value) ~= rule.type then
return false, "类型错误,期望 " .. rule.type .. ",实际 " .. type(value)
end
if rule.min and value < rule.min then
return false, "值太小,最小值为 " .. rule.min
end
if rule.max and value > rule.max then
return false, "值太大,最大值为 " .. rule.max
end
if rule.enum then
local found = false
for _, allowed in ipairs(rule.enum) do
if value == allowed then
found = true
break
end
end
if not found then
return false, "值不在允许的范围内: " .. table.concat(rule.enum, ", ")
end
end
return true
end
local function validate_object(obj, schema_obj, path)
path = path or ""
for key, rule in pairs(schema_obj) do
local current_path = path == "" and key or path .. "." .. key
local value = obj[key]
if rule.required and value == nil then
return false, "必填字段缺失: " .. current_path
end
if value ~= nil then
if type(rule.properties) == "table" then
local success, err = validate_object(value, rule.properties, current_path)
if not success then
return false, err
end
else
local success, err = validate_value(value, rule)
if not success then
return false, current_path .. ": " .. err
end
end
end
end
return true
end
return validate_object(current_config, schema)
end
-- 初始化默认配置
M.init()
return M
]]
local file = io.open("config_manager.lua", "w")
file:write(config_package)
file:close()
-- 测试配置管理包
local config = require("config_manager")
-- 基本使用
print("应用名称:", config.get("app.name"))
print("数据库端口:", config.get("database.port"))
print("不存在的配置:", config.get("nonexistent.key", "默认值"))
-- 设置配置
config.set("app.debug", true)
config.set("server.ssl.enabled", true)
print("设置后的调试模式:", config.get("app.debug"))
print("SSL启用状态:", config.get("server.ssl.enabled"))
-- 配置验证
local schema = {
app = {
properties = {
name = {type = "string", required = true},
version = {type = "string", required = true},
debug = {type = "boolean"}
}
},
database = {
properties = {
port = {type = "number", min = 1, max = 65535, required = true}
}
}
}
local valid, err = config.validate(schema)
print("配置验证结果:", valid and "通过" or ("失败: " .. err))
-- 保存配置
local save_success = config.save_to_file("app_config.lua")
if save_success then
print("配置已保存到 app_config.lua")
-- 读取保存的配置文件内容
local file = io.open("app_config.lua", "r")
local content = file:read("*a")
file:close()
print("保存的配置内容:")
print(content)
-- 清理
os.remove("app_config.lua")
end
-- 清理
os.remove("config_manager.lua")
package.loaded["config_manager"] = nil
end
create_config_package()5.2 日志记录包
lua
-- 日志记录包示例
local function create_logging_package()
print("\n=== 日志记录包 ===")
local logging_package = [[
-- logger.lua
local M = {}
-- 日志级别
M.LEVELS = {
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
FATAL = 5
}
local LEVEL_NAMES = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
local LEVEL_COLORS = {
DEBUG = "\27[36m", -- 青色
INFO = "\27[32m", -- 绿色
WARN = "\27[33m", -- 黄色
ERROR = "\27[31m", -- 红色
FATAL = "\27[35m" -- 紫色
}
local COLOR_RESET = "\27[0m"
-- 默认配置
local default_config = {
level = M.LEVELS.INFO,
format = "[%timestamp%] %level% %location% - %message%",
timestamp_format = "%Y-%m-%d %H:%M:%S",
output = "console", -- console, file, both
filename = "app.log",
max_file_size = 10 * 1024 * 1024, -- 10MB
max_files = 5,
colors = true
}
local config = {}
for k, v in pairs(default_config) do
config[k] = v
end
-- 输出处理器
local outputs = {}
-- 控制台输出
outputs.console = function(formatted_message, level)
local output = config.colors and
(LEVEL_COLORS[LEVEL_NAMES[level]] .. formatted_message .. COLOR_RESET) or
formatted_message
print(output)
end
-- 文件输出
outputs.file = function(formatted_message, level)
local file = io.open(config.filename, "a")
if file then
file:write(formatted_message .. "\n")
file:close()
end
end
-- 格式化消息
local function format_message(level, message, location)
local formatted = config.format
-- 替换占位符
formatted = string.gsub(formatted, "%%timestamp%%", os.date(config.timestamp_format))
formatted = string.gsub(formatted, "%%level%%", LEVEL_NAMES[level])
formatted = string.gsub(formatted, "%%location%%", location or "")
formatted = string.gsub(formatted, "%%message%%", message)
return formatted
end
-- 获取调用位置信息
local function get_location()
local info = debug.getinfo(4, "Sl")
if info then
local source = info.source
if string.sub(source, 1, 1) == "@" then
source = string.sub(source, 2)
end
return string.format("%s:%d", source, info.currentline or 0)
end
return "unknown"
end
-- 通用日志函数
local function log(level, message, ...)
if level < config.level then
return
end
-- 格式化消息
if select("#", ...) > 0 then
message = string.format(message, ...)
end
local location = get_location()
local formatted = format_message(level, message, location)
-- 输出到指定目标
if config.output == "console" or config.output == "both" then
outputs.console(formatted, level)
end
if config.output == "file" or config.output == "both" then
outputs.file(formatted, level)
end
end
-- 公共接口
function M.debug(message, ...)
log(M.LEVELS.DEBUG, message, ...)
end
function M.info(message, ...)
log(M.LEVELS.INFO, message, ...)
end
function M.warn(message, ...)
log(M.LEVELS.WARN, message, ...)
end
function M.error(message, ...)
log(M.LEVELS.ERROR, message, ...)
end
function M.fatal(message, ...)
log(M.LEVELS.FATAL, message, ...)
end
-- 配置管理
function M.configure(new_config)
for k, v in pairs(new_config) do
if config[k] ~= nil then
config[k] = v
end
end
end
function M.get_config()
local result = {}
for k, v in pairs(config) do
result[k] = v
end
return result
end
-- 设置日志级别
function M.set_level(level)
if type(level) == "string" then
level = M.LEVELS[string.upper(level)]
end
if level then
config.level = level
end
end
-- 创建子记录器
function M.create_logger(name)
local logger = {}
local function child_log(level, message, ...)
if level < config.level then
return
end
if select("#", ...) > 0 then
message = string.format(message, ...)
end
local prefixed_message = "[" .. name .. "] " .. message
local location = get_location()
local formatted = format_message(level, prefixed_message, location)
if config.output == "console" or config.output == "both" then
outputs.console(formatted, level)
end
if config.output == "file" or config.output == "both" then
outputs.file(formatted, level)
end
end
logger.debug = function(message, ...) child_log(M.LEVELS.DEBUG, message, ...) end
logger.info = function(message, ...) child_log(M.LEVELS.INFO, message, ...) end
logger.warn = function(message, ...) child_log(M.LEVELS.WARN, message, ...) end
logger.error = function(message, ...) child_log(M.LEVELS.ERROR, message, ...) end
logger.fatal = function(message, ...) child_log(M.LEVELS.FATAL, message, ...) end
return logger
end
return M
]]
local file = io.open("logger.lua", "w")
file:write(logging_package)
file:close()
-- 测试日志包
local logger = require("logger")
-- 基本日志记录
logger.debug("这是调试信息")
logger.info("应用程序启动")
logger.warn("这是警告信息: %s", "内存使用率高")
logger.error("发生错误: %s", "文件未找到")
-- 配置日志
logger.configure({
level = logger.LEVELS.DEBUG,
format = "[%timestamp%] [%level%] %message%",
colors = false
})
print("\n配置更改后:")
logger.debug("现在可以看到调试信息了")
logger.info("格式已更改")
-- 创建子记录器
local db_logger = logger.create_logger("DATABASE")
local http_logger = logger.create_logger("HTTP")
print("\n子记录器测试:")
db_logger.info("数据库连接已建立")
http_logger.warn("HTTP请求超时")
-- 文件输出测试
logger.configure({output = "file", filename = "test.log"})
logger.info("这条消息将写入文件")
-- 检查文件是否创建
local log_file = io.open("test.log", "r")
if log_file then
print("\n日志文件内容:")
print(log_file:read("*a"))
log_file:close()
os.remove("test.log")
end
-- 清理
os.remove("logger.lua")
package.loaded["logger"] = nil
end
create_logging_package()6. 总结
Lua的模块与包系统为代码组织和重用提供了强大的支持:
核心要点
- 模块化设计:将相关功能组织到独立的模块中
- 包管理:使用LuaRocks进行依赖管理
- 版本控制:合理的版本策略确保兼容性
- 性能优化:模块缓存和延迟加载提升性能
- 最佳实践:遵循命名规范和文档标准
实际应用
- 构建可维护的大型应用
- 创建可重用的库和工具
- 实现插件系统和扩展机制
- 管理项目依赖关系
掌握模块与包系统是Lua高级编程的重要基础。