7

我正在从一本书中学习 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

“writeToFile”在尝试写入 _G[resTable[i]] 时出错。在此处列出的前两个函数中,我不明白为什么它们引用 _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
4

3 回答 3

3

显然作者已经实现了一种将全局变量列表保存到文件并恢复它们的方法。

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

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

在这个循环resTable[i]中是第 i 个名称和_G[resTable[i]]对应的值,取自 table _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 脚本,并通过使用 Luadofile函数执行脚本来读取它们。它的主要限制是它只能保存字符串,布尔数字,但通常这在学习时就足够了。

您可以使用以下语句对其进行测试:

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 页面上的表序列化

于 2013-10-10T21:27:25.173 回答
0

此代码从文件读取数据并将数据写入全局变量,其名称在aryTable. 由于您的文件为空,readFromFile因此实际上并未设置变量值。然后writeToFile在尝试获取变量值时失败,因为它们尚未设置。

尝试将数据放入文件中以便设置变量,或者在将变量值写入文件之前自己设置变量值(例如Score = 10,等)

于 2013-10-10T20:37:23.123 回答
0

这些不是通用的“从/向任何文件读取/写入任何表”功能。他们显然期望全局表的名称作为参数,而不是 [对本地的引用] 表本身。它们看起来像是一种针对往往出现在书中的非常具体问题的一次性解决方案。:-)

你的函数不应该对 _G 做任何事情。我没有方便的 API 参考,但读取循环应该做类似的事情

resTable[a] = line

并且写循环会做

hfile:write(resTable[i])

也扔掉那个本地“结果”表。:-)

于 2013-10-10T20:04:10.567 回答