第十一章:Lua 实战项目

纸上得来终觉浅,绝知此事要躬行。学习了这么多理论知识,是时候通过一些实战项目来巩固我们的 Lua 技能了。

本章将通过三个难度递增的小项目,带领大家综合运用前面学到的知识:

  1. 命令行计算器:练习字符串解析和基本的数学运算。

  2. 文本统计工具:练习文件 I/O、字符串匹配和表的操作。

  3. 简单的配置文件解析器:练习表的结构化数据处理。

11.1 项目一:命令行计算器

功能描述

用户在命令行输入简单的四则运算表达式(如 10 + 20 * 3),程序计算并输出结果。为了简化,我们暂时只支持两个操作数和一个运算符。

代码实现

-- calculator.lua

function calculate(n1, op, n2)
    if op == "+" then
        return n1 + n2
    elseif op == "-" then
        return n1 - n2
    elseif op == "*" then
        return n1 * n2
    elseif op == "/" then
        if n2 == 0 then
            return nil, "Division by zero"
        end
        return n1 / n2
    else
        return nil, "Unknown operator"
    end
end

print("Simple Calculator (Enter 'exit' to quit)")
print("Format: number operator number (e.g., 10 + 5)")

while true do
    io.write("> ")
    local input = io.read()
    
    if input == "exit" then
        break
    end
    
    -- 解析输入: 捕获 数字 运算符 数字
    local n1_str, op, n2_str = string.match(input, "(%d+)%s*([%+%-%*/])%s*(%d+)")
    
    if n1_str and op and n2_str then
        local n1 = tonumber(n1_str)
        local n2 = tonumber(n2_str)
        
        local result, err = calculate(n1, op, n2)
        
        if result then
            print("Result: " .. result)
        else
            print("Error: " .. err)
        end
    else
        print("Invalid input format!")
    end
end

扩展挑战

  • 支持浮点数(修改模式匹配 %d+[%d%.]+)。

  • 支持多个运算符的优先级解析(这就需要用到逆波兰表达式或递归下降解析了,难度较高)。

11.2 项目二:文本统计工具

功能描述

读取一个文本文件,统计其中出现频率最高的 10 个单词。

代码实现

-- word_count.lua

local filename = "sample.txt" -- 假设文件名为 sample.txt
local f = io.open(filename, "r")

if not f then
    print("Cannot open file: " .. filename)
    os.exit()
end

local text = f:read("a") -- 读取所有内容
f:close()

local counts = {}

-- 遍历所有单词
for word in string.gmatch(text, "%a+") do
    word = string.lower(word) -- 转小写
    if string.len(word) > 2 then -- 忽略过短的单词
        counts[word] = (counts[word] or 0) + 1
    end
end

-- 将统计结果转换为数组以便排序
local sorted_words = {}
for w, c in pairs(counts) do
    table.insert(sorted_words, {word = w, count = c})
end

-- 排序:按频率降序
table.sort(sorted_words, function(a, b)
    return a.count > b.count
end)

-- 输出前 10 个
print("Top 10 words:")
for i = 1, math.min(10, #sorted_words) do
    local item = sorted_words[i]
    print(string.format("%-15s: %d", item.word, item.count))
end

扩展挑战

  • 支持命令行参数指定文件名(使用 arg 表)。

  • 过滤掉常见的停用词(如 “the”, “and”, “is” 等)。

11.3 项目三:简单的配置文件解析器

功能描述

解析类似于 INI 格式的配置文件,并将其转换为 Lua 表。

配置文件示例 (config.ini):

[server]
host = 127.0.0.1
port = 8080

[database]
user = admin
password = secret

代码实现

-- ini_parser.lua

function parse_ini(filename)
    local f = io.open(filename, "r")
    if not f then return nil, "Cannot open file" end
    
    local config = {}
    local current_section = nil
    
    for line in f:lines() do
        -- 去除空白字符
        line = string.match(line, "^%s*(.-)%s*$")
        
        -- 忽略空行和注释
        if line ~= "" and string.sub(line, 1, 1) ~= ";" then
            
            -- 匹配 [section]
            local section = string.match(line, "^%[(.+)%]$")
            if section then
                current_section = section
                config[current_section] = config[current_section] or {}
            else
                -- 匹配 key = value
                local key, value = string.match(line, "^(%w+)%s*=%s*(.+)$")
                if key and value then
                    if current_section then
                        config[current_section][key] = value
                    else
                        print("Warning: Key found outside section: " .. key)
                    end
                end
            end
        end
    end
    
    f:close()
    return config
end

-- 测试
local conf = parse_ini("config.ini")
if conf then
    print("Database User: " .. conf.database.user)
    print("Server Port: " .. conf.server.port)
end

扩展挑战

  • 支持布尔值和数字类型的自动转换(目前全部解析为字符串)。

  • 支持写入功能,将 Lua 表保存回 INI 文件。

总结

通过这三个项目,我们练习了:

  1. 字符串处理string.match, string.gmatch 是文本处理的神器。

  2. 表的操作:用作字典计数、用作数组排序、用作结构化数据存储。

  3. 文件 I/O:读取文件内容、按行迭代。

  4. 逻辑控制:循环、条件判断、函数封装。

希望这些项目能让你对 Lua 的实际应用有更深的体感。


下一章预告:作为教程的最后一章,我们将总结 Lua 的最佳实践,探讨性能优化技巧,并以此结束我们的 Lua 基础之旅。