C遍历Lua中的Table

 

使用Lua作为配置文件真的是酷死,比什么XML,INI爽多了。用户可以有更多的控制,解析也更轻松,又安全。缺点我是没有发现啦,发现的朋友告知一声哦!

C获取Lua中的一般全局变量(boolean, number, string)

下面以字符串为例。

Lua 文件 config.lua

app_name = "Test"
app_author = "Gotaly"

解析配置文件的C文件 config.c (这里不作出错处理,聚焦于Lua的C API和其逻辑的运用)

#include <stdio.h>
#include <string.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>


int main(int argc,char *argv[])
{

    lua_State *lua = luaL_newstate();
    /**
    * luaL_openlibs(lua);
    */
    luaL_loadfile(lua,"config.lua");
    lua_pcall(lua,0,0,0);

    lua_getglobal(lua,"app_name");
    lua_getglobal(lua,"app_author");
    printf("application name is %s,author is %s \n",lua_tostring(lua,-2),lua_tostring(lua,-1));
    return 0;
}

这个例子和《programming in lua》中的很类似(其实同样功能的代码都类似,呵呵),这里解释下各 API的作用。

1) lua_State *lua = luaL_newstate();

这个在你的C程序中创建一个Lua环境,之后就可以通过该句柄和Lua进行通信了。

2) luaL_openlibs(lua);

打开Lua中要使用的标准库,具体到我们这个例子可以不使用。

3) luaL_loadfile(lua,"config.lua");lua_pcall(lua,0,0,0);

加载Lua程序并执行,这里要做出错处理。例子中为了突出逻辑,没有做相应处理。同样除了loadfile,还有loadbuffer,loadstring等加载Lua程序的接口,可以自行查阅Manual

4)重点来了lua_getglobal(lua,"app_name");

这里会从句柄lua表示的环境中获取全局变量"app_name",这里说是全局变量,但是它只是相对于该句柄所表示的饿环境中的全局变量。 调用了该接口后,相应的变量的值就被压入到Lua和C交流的栈空间了。后面将其出栈就可以在C程序中得到该值。

5)lua_tostring(lua,-2)

这里就根据之前入栈的顺序,将句柄lua和C交互中的栈空间里相应的值出栈。lua_toxxxx是一个系列函数,具体可以参考Lua的Manual,这里主要将其内容转换成字符串。

C获取Lua中的Table

讲完了获取一般的变量,这里切入正题,获取Table内容。

Lua 文件 config.lua

app_content = {
    name = "app_Test",
    author = "Gota"    
}

解析配置文件的C文件 config.c (

#include <stdio.h>
#include <string.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>


int main(int argc,char *argv[])
{

    lua_State *lua = luaL_newstate();
    /**
    * luaL_openlibs(lua);
    */
    luaL_loadfile(lua,"config.lua");
    lua_pcall(lua,0,0,0);

    lua_getglobal(lua,"app_content");
    lua_getfield(lua,-1,"name");
    lua_getfield(lua,-2,"author");
    printf("application name is %s,author is %s \n",lua_tostring(lua,-2),lua_tostring(lua,-1));
    return 0;
}

这里对上面的代码进行说明。主要就是多了一个API的使用

6)lua_getfield(lua,-1,"name"); 该API主要是用来处理Table。其第一个参数是交互的句柄,第二个参数是Table在交互的栈的位置,第三个参数是前面Table中的键。 该函数的结果是将该Table中对于键的值取出来,并压入到交互栈中。这样就使得原来位于(-1)位置的Table就下压了一个位置到了 (-2)。

再说明下上述程序的整个执行过程,首先弓getglobal得到一个全局变量app_content,然后将他压入到交互栈中,位于(-1)位置。 然后调用lua_getfield(lua,-1,"name");,将(-1)位置的变量作Table解析,并取出其中的“name”键的值压入交互栈,位于(-1)位置, 原来的Table被下压一层至(-2)。然后调用lua_getfield(lua,-2,"author");对(-2)位置的Table进行取值,取出其键为“author”的 值并压入到交互栈位置(-1),这样原来的“name”就被压入到(-2)位置。最后用tostring,将栈中的数据取出来。

这里我省掉了2个API。一个是lua_isxxx系列,这个系列中的lua_istable( lua_State *lua,int index),可以检查index位置的变量是否 为Table。上面我们主要就是对table作测试就没有进行验证了。

第二个是lua_pop(lua_State *lua,int num),从交互句柄的交互栈中弹出几个值,这个函数在下面的例子中将会用到。

C获取Lua中的嵌套Table

Lua 文件 config.lua

app_content = {
    name = "app_Test",
    author = "Gota"    
}

app_config.content = app_content

解析配置文件的C文件 config.c

#include <stdio.h>
#include <string.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>


int main(int argc,char *argv[])
{

    lua_State *lua = luaL_newstate();
    /**
    * luaL_openlibs(lua);
    */
    luaL_loadfile(lua,"config.lua");
    lua_pcall(lua,0,0,0);

    lua_getglobal(lua,"app_content");
    lua_pushnil(lua);
    while(lua_next(lua,-2)){
        printf("get key %s\n is table %d\n",lua_tostring(lua,-2),lua_istable(lua,-1));
        lua_getfield(lua,-1,"name");
        printf("application name is %s \n",lua_tostring(lua,-2));

        lua_pop(lua,1);    
    }

    return 0;
}

对于嵌套解析Table,大家看以看过来,作者对其机制解释的特别清楚。这里我只说下我 看了文章后的学到的内容。

7)lua_pushnil(lua_State *lua);

看过《programming in lua》都知道lua_pushxxx系列函数,可以在C中将一些值压入交互栈。pushnil就是向栈中压入Lua的nil,nil在Lua中是一个类型值,在C中,大家可以 将其视为NULL,并且像lua_tostring这样的从栈中未取到值,也就是Lua中的nil,得到的结果就是NULL。

8) lua_next(lua_State *lua,int index)

lua_next(lua_State *lua,int index)函数是这个例子的主角,他可以根据指定交互栈中index处的Table,进行遍历,每次取(-1)位置的一个key作为前辈,即将要取得一对元素的上一对元素的key,然后返回Table的该 对元素,将其键先压入栈,再将该键对应的值压入栈,结果就是(-2)位置放的是键,(-1)位置放的是值。Table自然被压入到其后,本例中的(-3)位置。如果key为nil,则默认为首对数据, 会随机的压入一对值。当所有值都被遍历一遍后,next返回0。

9)lua_pop(lua_State *lua,int num)

该函数如上面所述:从交互句柄的交互栈中弹出num个值。这里不得不说下Lua作配置文件的另一个好处-Lua自己处理堆栈,使得配置文件程序更安全。所有压入栈中的内容,只要 调用该函数,Lua就是自己对其内存进行处理,无需程序员得干预,当然,这样也说明了,不可以带走栈中的内存,也就是不可以将栈中弹出来的内存如字符串内存用作他用,否则可能 在pop后,该内存将失效。

这里将整个程序逻辑说一遍

首先lua_getglobal(lua,"app_content");将配置文件中app_content变量压入交互栈(-1)位置,然后lua_pushnil(lua);将nil压入交互栈(-1)位置以供后续的lua_next作首个元素使用。 app_content被压入下一层(-2),接着进while循环,用lua_next进行迭代,初次遇到nil,会随机的取得一对键值,然后将键和值压入交互栈,得到的结果就是:(-2)位置为key,(-1) 位置为value.Table自然被压入(-3)位置。在处理完value(如printf)后,用pop将value弹出,留下key用作下一对值的前辈,这时,(-1)位置为当前key,(-2)位置为Table,再次进 while循环的next函数,去处理下一对键值。最后当所有键值对都处理完全后,next返回0,退出while循环。

总结

变量Table的一个总的结构就是

lua_getglobal(L, table_name);
lua_pushnil(L);
while (lua_next(L, -2)) {
    /* 处理相应数据。此时栈上 -1 处为 value, -2 处为 key */
    lua_pop(L, 1);
}

当然,这里的索引是根据需要进行变更的。

转载于:https://www.cnblogs.com/gotaly/articles/2647475.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
详细描述Lua和C之间相互传递Table类型数据 /* ====================================================== */ // 遍历Lua传入的Table类型参数, 获取它的Key/Value, 其关键操作是 lua_next() // lua_next() 返回1表示读取成功,返回0表示已经没有数据可读了 // lua_next() 会使用栈顶元素作为Key去定位本次需要取出Table里面的那个值对 // 如果Key=nil, 那就表示本次取出的是第一个元素 // 它会先将当前的这个Key弹出,然后将本次取出的Key/Value压入栈, Value在栈顶 // 一个比较隐晦的处理就是, 我们不应直接使用lua_tostring(L, -2)来读取Key // 因为lua_tostring()在Key类型不是字符串时, 它会修改栈上的Key数据 // 这样, 下次调用lua_next()时, 就会因为Key被修改了而导致错误 // 为此,先调用lua_pushvalue(L, -2),将它Copy一份到栈顶,对这个Copy进行lua_tostring() // 读取Key,Value到C变量里面后,将Value和Copy弹出,留着Key在栈顶,给下次lua_next()用 // // 指令及栈图变化如下: (假如Table的栈下标是Index) // 0. 刚进入函数时 ...Table, ... <--- 这里栈顶 // 1. lua_pushnil(L) ...Table, ..., nil <--- 这里栈顶 // 2. lua_next(L, Index) ...Table, ..., Key, Value <--- 这里栈顶 // 3. lua_pushvalue(L, -2) ...Table, ..., Key, Value, KeyCopy <--- 这里栈顶 // 4. lua_pop(L, 2), ...Table, ..., Key <--- 这里栈顶 // ... 如此重复2,3,4 // N. lua_next(L, Index)返回0 ...Table, ... <--- 这里栈顶 /* ====================================================== */

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值