Lua学习笔记 第十二章 数据文件与持久性

12.1 数据文件

10.1节介绍的那样,可以由table构造式来定义一种文件格式。这项技术也就是将数据

作为Lua代码来输出,当运行这些代码时,程序也就读取了数据。示例——原来的数据文件格式:

Donald E.Knuth, Literate Programming, CSLI, 1992

Jon Bentley, More Programming Pearls, Addisom-Wesley,1990

现在可以改写为:

Entry{"Donald E.Knuth",

     "Literate Programming",

     "CSLI",

      1992}

Entry{"Jon Bentley",

      "MoreProgramming Pearls",

     "Addison-Wesley",

      1950}

注意: Entry{<code>} Entry({<code>}) 是完全等价的,都是以一个table作为参数来调用函数Entry

为了读取该文件我们只需定义一个合适的Entry,然后运行此程序就可以了。

以下这个程序计算了数据文件中条目的数量:

local count = 0

function Entry(_) count = count + 1 end

dofile("data")      -- 文件data里的数据就是上面Entry里面的数据

print("number of entries:"..count)

下一个程序则收集数据文件中所有者的姓名,然后打印出这些姓名:

local authors = {}

function Entry(b) authors[b[1]] = true end

dofile("data")

for name in pairs(authors) do print(name) end

这些代码片段都采用了时间驱动的做法。Entry函数作为一个回调函数,在dofile时为数据

文件中的每个条目所调用。

若文件不是非常大,可以使用名值对来表示每个字段:

Entry{

  author ="Donald E.knuth",

  title ="Literate Programming",

  publisher ="CSLI",

  year = 1992}

Entry{

  author ="Jon Bentley",

  title ="More Programming Pearls",

  year = 1990,

  publisher ="Addison-Wesley"}

这样收集作者姓名的程序改为:

local authors = {}

function Entry(b)

    if b.author then authors[b.author] = true end

end

dofile("data")

for name in pairs(authors) do print(name) end

 

12.2 串行化

串行化数据(serialization)就是将数据转换为一个字节流或字符流。然后就可以将其存储到一个文件中,

或者通过网络连接发送出去。

串行化后的数据可以用Lua代码来表示,这样当运行这些代码时,存储的数据就可以在读取程序中得到重构。

如果要恢复一个全局变量的值,那么串行化的结果或许可以是"varname =<exp>",其中<exp>是一段用于创建

该值的Lua代码,varname只是一个简单的标示符。示例:

function serialize(o)

    if type(o) =="number" then

        io.write(o)

    elseif type(o) == "string" then

        io.write(string.format("%q",o))

    else

        <其它情况>

    end

end

string.format("%q", str) 函数会用双引号来括住字符串,并且正确地转移其中的双引号和换行符等其它特殊字符。


此外Lua5.1 还提供了另外一种可以以一种安全的方法来括住任意字符串的方法。这种新的标记方式[=[ ... ]=]

用于长字符串。然而这种新方式主要是为手写的代码提供方便的,通过它就不需要改变任何字符串的内容。

但在自动生成的代码中还是推荐使用string.format("%q", str),更方便.在使用这种方法时要注意两个细节问题。

首先,必须使用正确数量的等号。这个正确的数量应比字符串中出现的最长的等号序列还大1

其次,Lua总是会忽略所有长字符串开头的换行符,避免这个问题的简单方法就是在字符串起始处添加一个换行符。

以下quote函数就是根据这两个注意点编写的处理函数:

function quote(s)       -- 查找最长的等号序列

    local n = -1

    for w instring.gmatch(s, "]=*") do

n =math.max(n, #w - 1)

    end

    -- 产生n+1个等号

    local eq =string.rep("=", n+1)

    -- 生成长字符串的字面表示,在字符串开始的地方加了一个换行符

    returnstring.format("[%s[\n%s]%s]", eq, s, eq)

end

 

12.2.1 保存无环的table

下一个任务是保存table。保存table的方法有几种,选用哪种方法取决于table的结构。简单的table

不仅需要简单的算法,而且更需要完美地输出结果。

第一个算法如下:

function serialize(o)

    if type(o) =="number" then

io.write(o)

    elseiftype(o) == "string" then

        io.write(string.format("%q",o))

    elseiftype(o) == "table" then

        io.write("{\n")

        for k, vin pairs(o) do

io.write("", k, " = ")

            serialize(v)

io.write(",\n")

        end

        io.write("}\n")

    else

error("cannotserialize a " .. type(o))

    end

end

上面函数假设了一个table中的所有key都是合法的标示符。但如果一个tablekey为数字或者为非法

Lua标识符,那么就会出现问题。一个简单的解决方法是将这行代码:

io.write(" ", k, " = ")改为:

io.write(" ["); serialize(k);io.write("] = ")

这样便增强了这个函数的强健性,但却损失了结果文件的美观性。例如对于调用:

serialize{a=12, b='Lua', key='another"one"'}

第一个版本的serialize会输出:

{

    a = 12,

    b ="Lua",

    key ="another \"one\"",

}

而第二个版本则输出:

{

    ["a"]= 12,

    ["b"]="Lua",

    ["key"]= "another \"one\"",

}

 

12.2.2 保存有环的table

如果要处理任意拓扑结构(带环的table或共享子table)table,就需要采用另外一种方法。

保存函数要求将待保存的值及其名称一起作为参数传入;此外还必须持有一份所有已保持过的table

名称记录,以此来检测环并复用其中的table

代码如下:

function basicSerialize(o)

    if type(o) =="number" then

        return tostring(o)

    else                                -- 假设 o 是字符串

        returnstring.format("%q", o)

    end

end

functin save(name, value saved)

    saved = savedor {}                 --初始值

    io.write(name,"=")

    iftype(value) == "number" or type(value) == "string" then

        io.write(basicSerialize(value),"\n")

    elseiftype(value) == "table" then

        if saved(value) then              -- value是否已经保存过?

            io.write(saved[value], "\n")  -- 使用先前的名字

        else

            saved[value] = name            -- 为下次使用保持名字

            io.write("{}\n")         -- 创建一个新的table 

            for k, v in pairs(value) do   -- 保存其字段

                k = basicSerialize(k)

                local fname = string.format("%s[%s]",name, k)

                save(fname,v, saved)

            end

        end

    else

error("cannotsave a ".. type(value))

    end

end

示例——假设有一个table如下所示:

a = {x=1, y=2, {3,4,5}}

a[2] = a        --

a.z = a[1]      -- 共享子table

 

调用save("a", a) 保存的结果为:

a = {}

a[1] = {}

a[1][1] = 3

a[1][2] = 4

a[1][3] = 5

a[2] = a

a["y"] = 2

a["x"] = 1

a["z"] = a[1]

如果想以共享的方式来保存几个table中的共同部分,只需调用save时使用相同的saved

参数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Lua中,可以使用io库中的writefile函数将数据写入文件。根据引用\[1\]中的代码,可以自定义一个函数writeLog2File来实现将数据写入文件的功能。该函数接受一个参数msg,表示要写入文件的内容。在函数内部,使用io.writefile函数将msg追加写入到本地文件中。具体的代码如下: ```lua function writeLog2File(msg) local logFilePath = cc.FileUtils:getInstance():getWritablePath() .. "debug_log.txt" io.writefile(logFilePath, string.format("%s: %s\n", os.date(), msg), "a+") end ``` 在调用writeLog2File函数时,传入要写入文件的数据即可。例如,要将字符串"Hello, Lua!"写入文件,可以使用以下代码: ```lua writeLog2File("Hello, Lua!") ``` 这样,字符串"Hello, Lua!"就会被追加写入到debug_log.txt文件中。 #### 引用[.reference_title] - *1* [cocos2d-lua:将打印的Lua日志写入本地文件](https://blog.csdn.net/fightsyj/article/details/90273487)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Lua将Nginx请求数据写入Kafka——埋点日志解决方案](https://blog.csdn.net/u013014691/article/details/122735525)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值