遍历 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 表的主要内容,如果未能解决你的问题,请参考以下文章