C语言 遍历Lua表

wbrvyc0a  于 2023-03-17  发布在  其他
关注(0)|答案(4)|浏览(167)

我尝试迭代lua表,但一直收到以下错误:

invalid key to 'next'

我知道index从-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?
laik7k3q

laik7k3q1#

有两件事你需要注意:

  • 在下一次调用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
}
mwecs4sa

mwecs4sa2#

不要使用带负参数的luaL_checkstring。请改用lua_tostring
此外,确保在循环中调用函数后堆栈保持不变:lua_next期望堆栈顶部的前一个表键,以便它可以继续遍历。

ehxuflar

ehxuflar3#

来自手册:

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

将给定可接受索引处的Lua值转换为C字符串。如果len不为NULL,则还将字符串长度设置为 *len。Lua值必须是字符串或数字;如果值是一个数字,那么lua_tolstring也会将堆栈中的实际值更改为字符串。(当lua_tolstring在表遍历期间应用于键时,此更改会混淆lua_next。)
luaL_checkstring调用lua_tolstring

lskq00tm

lskq00tm4#

另请参见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

相关问题