Lua 5.1中自定义C版loadfile函数报错求助
问题排查:自定义Lua loadfile函数导致的EOF错误
问题场景
在Lua 5.1环境下,用C语言实现自定义loadfile函数替换原生函数后,运行测试Lua代码时出现错误:[string "custom_table":165: '<eof>' expected]。尝试在luaL_loadbuffer后调用lua_tostring,又因数据非字符串触发另一个错误。
相关代码
C实现代码
#include <stdio.h> #include <string.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> static void panic(char* error) { fprintf(stderr, "panic: %s\n", error); exit(1); } static int _custom_loadfile(lua_State* L) { const char* rfile = luaL_checkstring(L, 1); FILE* rfile_fp = fopen(rfile, "r"); if (rfile_fp == NULL) panic("failed to open rfile."); if (fseek(rfile_fp, 0, SEEK_END) != 0) panic("failed to seek to eof."); size_t buffersize = ftell(rfile_fp) + 1 * sizeof(char); char* buffer = (char*) malloc(buffersize); if (buffer == NULL) lua_error(L); if (fseek(rfile_fp, 0, SEEK_SET) != 0) panic("failed to seek to start."); if (fread(buffer, 1, buffersize, rfile_fp) != buffersize) panic("failed to read file"); int error = luaL_loadbuffer(L, buffer, bufferfilesize, rfile); if (error) lua_error(...); return 1; // one value } // ... 省略其他代码 int main(int argc, char* argv[]) { lua_State* L = luaL_newstate(); luaL_openlibs(L); lua_pushcfunction(L, _custom_loadfile); lua_setglobal(L, "loadfile"); // ... 省略其他代码 }
Lua测试代码
example.lua
-- ... 省略其他代码 custom_table = loadfile('custom_table') print(custom_table) -- ... 省略其他代码
custom_table.lua
return { name = 'custom_table', other = 'lots of stuff', table = {}, }
错误原因分析
- 未定义变量使用:调用
luaL_loadbuffer时用了未声明的bufferfilesize,而非正确的buffersize,导致Lua读取字节数为随机值,超出文件实际长度触发EOF错误。 - 字符串终止符缺失:计算
buffersize时预留了终止符空间,但fread后未手动添加\0,Lua解析时可能读到垃圾数据。 - 读取判断逻辑错误:文件实际大小为
buffersize-1,fread最多读取buffersize-1字节,用!= buffersize判断会误判读取失败。 - 资源未释放:打开文件后未调用
fclose,存在资源泄漏;错误分支未释放已分配内存。 - 错误处理不规范:
lua_error(...)写法错误,未正确传入Lua状态机和错误信息。
修复后的代码
static int _custom_loadfile(lua_State* L) { const char* rfile = luaL_checkstring(L, 1); // 用二进制模式打开,避免换行符转换导致的长度计算偏差 FILE* rfile_fp = fopen(rfile, "rb"); if (rfile_fp == NULL) { lua_pushfstring(L, "failed to open file: %s", rfile); lua_error(L); } if (fseek(rfile_fp, 0, SEEK_END) != 0) { fclose(rfile_fp); lua_pushstring(L, "failed to seek to eof"); lua_error(L); } long file_size = ftell(rfile_fp); if (file_size < 0) { fclose(rfile_fp); lua_pushstring(L, "failed to get file size"); lua_error(L); } size_t buffersize = (size_t)file_size + 1; char* buffer = (char*)malloc(buffersize); if (buffer == NULL) { fclose(rfile_fp); lua_pushstring(L, "failed to allocate memory"); lua_error(L); } if (fseek(rfile_fp, 0, SEEK_SET) != 0) { fclose(rfile_fp); free(buffer); lua_pushstring(L, "failed to seek to start"); lua_error(L); } size_t read_bytes = fread(buffer, 1, (size_t)file_size, rfile_fp); if (read_bytes != (size_t)file_size) { fclose(rfile_fp); free(buffer); lua_pushstring(L, "failed to read file"); lua_error(L); } // 手动添加字符串终止符 buffer[read_bytes] = '\0'; // 传入实际文件长度,而非buffersize int error = luaL_loadbuffer(L, buffer, (size_t)file_size, rfile); // 及时释放资源 fclose(rfile_fp); free(buffer); if (error) { lua_error(L); } return 1; }
额外注意事项
- 用二进制模式
"rb"打开文件,避免跨系统换行符转换导致的文件长度计算错误。 - 所有资源(文件指针、内存)在错误分支必须对应释放,防止泄漏。
luaL_loadbuffer的第三个参数是文件实际内容长度(不含终止符),无需传入buffersize。
内容的提问来源于stack exchange,提问作者Tippecanoe
相关产品推荐
相关产品推荐

