我正在从书中学习Lua,但我不是程序员。我正在尝试使用以下功能(直接从书中复制)将数据表保存到文件中,但是当尝试从_G [resTable]获取字符串时,该功能出现错误。为什么?

function readFromFile(filename,resTable)
    local hfile = io.open(filename)
    if hfile == nil then return end
    local results = {} -why is this table here?
    local a = 1
    for line in hfile:lines() do-- debug shows this loop doesn't run (no lines in hfile?)
        _G[resTable[a]] = line
        a = a + 1
    end
end

function writeToFile(filename, resTable)
    local hfile = io.open(filename, "w")
    if hfile == nil then return end
    local i
    for i=1, #resTable do
        hfile:write(_G[resTable[i]])--bad argument #1 to 'write' (string expected, got nil)
    end
end

尝试对_G [resTable [i]]进行写操作时'writeToFile“出现错误。在此处列出的前两个函数中,我不明白为什么它们引用_G [resTable [i]],因为我看不到正在写入_G的任何代码。

因此,这是执行顺序:
local aryTable = {
"Score",
"Lives",
"Health",
}

readFromFile("datafile", aryTable)

writeToFile("datafile", aryTable)

我得到一个错误:
bad argument #1 to 'write' (string expected, got nil)
stack traceback:
[C]: in function 'write'
test.lua:45: in function 'writeToFile'
test.lua:82: in main chunk

最佳答案

显然,作者实现了一种保存全局变量列表到文件并将其还原的方法。

函数writeToFile需要一个文件名和一个全局变量名称列表(resTable)。然后,它打开一个用于写入的文件名并遍历提供的名称:

for i=1, #resTable do
    hfile:write(_G[resTable[i]])
end

在此循环中,resTable[i]是第i个名称,而_G[resTable[i]]是对应的值,该值取自表_G,该表存储所有全局变量。如果未定义具有该名称的全局变量,_G[resTable[i]]将返回nil,这是您遇到失败的原因。因此,您必须提供一个resTable并填充现有全局变量的名称,以避免出现此错误。

除此之外,作者的序列化策略真的很幼稚,因为它仅处理具有字符串值的变量。实际上,通过像丢失类型信息一样将变量保存到文件中,因此具有"100"值(字符串)和具有100值(数字)的变量将被存储在磁盘上。

分析readFromFile函数可以明显看出问题。打开文件以进行读取后,它将逐行扫描它,并为其resTable列表中提到的每个名称创建一个新变量:
local a = 1
for line in hfile:lines() do
    _G[resTable[a]] = line
    a = a + 1
end

问题有很多:
  • 循环变量line将始终具有字符串值,因此重新创建的全局变量将全部为字符串,即使它们最初是数字也是如此。
  • 假定以相同的顺序重新创建了变量,因此您必须在保存文件时使用的resTable中提供相同的名称;
  • 假定每行存储一个值,但这是一个错误的假设,因为writeToFile函数不会在每个值之后写入换行符;

  • 此外,local results = {}是无用的,并且在两个函数中,文件句柄hfile均未关闭。后者是非常不好的做法:它可能浪费系统资源,并且如果您的脚本失败,则假定写入的数据的某些部分将永远无法进入磁盘,因为它们仍可能卡在某些缓冲区中。脚本结束时,文件句柄将自动关闭,但前提是必须以合理的方式结束。

    除非您在粘贴代码时犯了一些错误或省略了代码的重要部分,或者除非本书正在逐步构建一些示例,否则我敢说它相当糟糕。

    如果您想要一种快速而肮脏的方法来保存和检索某些全局变量,则可以使用以下方法:
    function writeToFile( filename, resTable )
        local hfile = io.open(filename, "w")
        if hfile == nil then return end
        for _, name in ipairs( resTable ) do
            local value = _G[name]
            if value ~= nil then
                hfile:write( name, " = ")
                local vtype = type( value )
                if vtype == 'string' then
                    hfile:write( string.format( "%q", value ) )
                elseif vtype == 'number' or vtype == 'boolean' then
                    hfile:write( tostring( value ) )
                else
                    -- do nothing - unsupported type
                end
                hfile:write( "\n" )
            end
        end
        hfile:close()
    end
    
    readFromFile = dofile
    

    它将全局变量另存为Lua脚本,并通过使用Lua dofile函数执行脚本将其读回。它的主要限制是它只能保存字符串,对数字进行 bool(boolean) 运算,但是通常在学习时就足够了。

    您可以使用以下语句对其进行测试:
    a = 10
    b = "20"
    c = "hello"
    d = true
    print( a, b, c, d )
    writeToFile( "datafile", { "a", "b", "c", "d" } )
    a, b, c, d = nil
    print( a, b, c, d )
    readFromFile( "datafile" )
    print( a, b, c, d )
    

    如果您需要更高级的序列化技术,可以引用Lua WIKI page on table serialization

    08-04 12:49