遍历 Lua 表

Posted

技术标签:

【中文标题】遍历 Lua 表【英文标题】:Iterate through Lua Table 【发布时间】:2011-09-02 12:29:56 【问题描述】:

我正在尝试遍历一个 lua 表,但我不断收到此错误:

invalid key to 'next'

我知道索引从 -8 开始,并且我知道那里有一个表,因为它获得了其中的第一个(也是唯一的)值。但是,即使我知道表中只有一个字符串,它也会尝试再次循环。

if (lua_istable(L, index))

    lua_pushnil(L);

    // This is needed for it to even get the first value
    index--;

    while (lua_next(L, index) != 0)
    
        const char *item = luaL_checkstring(L, -1);
        lua_pop(L, 1);

        printf("%s\n", item);
    

else

    luaL_typerror(L, index, "string table");

任何帮助将不胜感激。

当我使用正索引时,这很好用(只要我不从中删除 1)

编辑:我注意到,如果我不考虑 item 的值,我不会收到此错误。只有当我开始读取 item 的值时,我才会收到此错误。当我从表中得到值时,我调用另一个 Lua 函数,这会破坏 lua_next 吗?

【问题讨论】:

【参考方案1】:

有两件事你需要注意:

确保在下次调用lua_next 之前将原始密钥留在堆栈中。 luaL_checkstring 会将非字符串键转换为字符串(因为结果字符串不在表中,它会成为无效键。)这很容易通过传递 luaL_checkstring 键的副本而不是原始键来完成。 确保在每次通过循环时保留堆栈结构(即弹出尽可能多的值)

您的函数仅适用于 index 的负值。你是正确的index--; 将确保index 在按下键后仍然指向表,但前提是index 是负数(即相对于堆栈的顶部)。如果index 是绝对的或伪索引,那么这将导致它指向错误的项目。最简单的解决方法是将另一个对表的引用推送到堆栈顶部。

这是一个用于演示的最小 C 程序:

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

static void iterate_and_print(lua_State *L, int index);

int main(int ac, char **av)

   lua_State *L = luaL_newstate();
   luaL_openlibs(L);

   // Create a table and put it on the top of the stack
   luaL_loadstring(L, "return one=1,[2]='two',three=3");
   lua_call(L, 0, 1);

   iterate_and_print(L, -1);
   return 0;


static void iterate_and_print(lua_State *L, int index)

    // Push another reference to the table on top of the stack (so we know
    // where it is, and this function can work for negative, positive and
    // pseudo indices
    lua_pushvalue(L, index);
    // stack now contains: -1 => table
    lua_pushnil(L);
    // stack now contains: -1 => nil; -2 => table
    while (lua_next(L, -2))
    
        // stack now contains: -1 => value; -2 => key; -3 => table
        // copy the key so that lua_tostring does not modify the original
        lua_pushvalue(L, -2);
        // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
        const char *key = lua_tostring(L, -1);
        const char *value = lua_tostring(L, -2);
        printf("%s => %s\n", key, value);
        // pop value + copy of key, leaving original key
        lua_pop(L, 2);
        // stack now contains: -1 => key; -2 => table
    
    // stack now contains: -1 => table (when lua_next returns 0 it pops the key
    // but does not push anything.)
    // Pop table
    lua_pop(L, 1);
    // Stack is now the same as it was on entry to this function

【讨论】:

luaL_checkstring 确实接受负索引,但如果失败,它会中止并给出错误消息,因为它旨在检查函数参数,而不是用于一般转换。 @lhf,好的,使用luaL_checkstring 似乎没有任何优势,除了它在问题的代码示例中。将其替换为lua_tostring【参考方案2】:

不要将luaL_checkstring 与否定参数一起使用。请改用lua_tostring

此外,请确保在循环中调用函数后堆栈保持不变:lua_next 期望前一个表键位于堆栈顶部,以便它可以恢复遍历。

【讨论】:

是的,前后转储堆栈的内容后,我注意到调用函数时有剩余。【参考方案3】:

来自manual:

const char *lua_tolstring (lua_State *L, int index, size_t *len);

在给定的位置转换 Lua 值 C 字符串的可接受索引。如果伦 不为 NULL,它还设置 *len 为 字符串长度。 Lua 值必须 是字符串或数字;否则, 该函数返回 NULL。如果 value 是一个数字,然后是 lua_tolstring 也改变了实际值 堆栈到一个字符串。 (此更改 当 lua_tolstring 混淆 lua_next 在表格中应用于键 遍历。)

luaL_checkstring 呼叫lua_tolstring

【讨论】:

抱歉,正如引用所述,这仅适用于键,并且您正在对值运行它。【参考方案4】:

另请参阅lua_next 文档中的示例,摘录于此处:

int lua_next (lua_State *L, int index);

从堆栈中弹出一个键,并从给定索引处的表中推送一个键值对(给定键之后的“下一个”对)。如果表中没有更多元素,则 lua_next 返回 0(并且不推送任何内容)。

典型的遍历如下所示:

/* table is in the stack at index 't' */
 lua_pushnil(L);  /* first key */
 while (lua_next(L, t) != 0) 
   /* uses 'key' (at index -2) and 'value' (at index -1) */
   printf("%s - %s\n",
          lua_typename(L, lua_type(L, -2)),
          lua_typename(L, lua_type(L, -1)));
   /* removes 'value'; keeps 'key' for next iteration */
   lua_pop(L, 1);
 

在遍历表时,不要直接在键上调用lua_tolstring,除非你知道键实际上是一个字符串。回想一下lua_tolstring 可能会更改给定索引处的值;这会混淆对lua_next 的下一次调用。

请参阅函数next,了解在遍历过程中修改表的注意事项。

【讨论】:

以上是关于遍历 Lua 表的主要内容,如果未能解决你的问题,请参考以下文章

lua:遍历表中的所有对

基于另一个计数值,遍历嵌套表 - LUA

写一lua函数,能够遍历打印一个table数据,求代码!

Lua:降序表遍历第2轮:迭代器对象

如何遍历Lua中的表?

在lua中怎样遍历访问table表中元素