Lua5.4 Windows终端下打开目录读取JSON转Lua表的正确语法
Lua 5.4(64位Windows)读取本地JSON转Lua表 可运行方案
常见报错根因
- 路径写法错误:Windows路径中的反斜杠
\是Lua字符串的转义标识符,直接写C:\Users\Lichr\Documents是完全错误的写法,会被Lua解析为包含非法转义序列的错误路径,无法定位到目标文件 - 文件操作流程不完整:仅调用
io.open未做返回值校验、未读取全量文件内容、未手动/自动关闭文件句柄,会直接触发运行时错误 - 自定义JSON解析逻辑兼容问题:自行实现的解析函数如果未覆盖Windows文件默认带的UTF-8 BOM头、全类型JSON值(null、布尔、嵌套结构)、所有合法转义字符,会出现解析失败
- 读取模式错误:Windows下用默认文本模式读取文件可能自动修改换行符,导致JSON内容被篡改解析失败
可直接运行的完整代码
-- 内置JSON解析与序列化实现,兼容Lua 5.4 local json = { _version = "0.1.0" } -- 内部类型判断函数 local function kind_of(obj) if type(obj) ~= "table" then return type(obj) end local i = 1 for _ in pairs(obj) do if obj[i] ~= nil then i = i + 1 else return "table" end end return "array" end -- 内部字符串转义函数 local function escape_str(s) local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'} local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'} for i, c in ipairs(in_char) do s = s:gsub(c, '\\' .. out_char[i]) end return s end -- 内部分隔符跳过函数 local function skip_delim(str, pos, delim, err_if_missing) pos = pos + #str:match('^%s*', pos) if str:sub(pos, pos) ~= delim then if err_if_missing then error('Expected ' .. delim .. ' near position ' .. pos) end return pos, false end return pos + 1, true end -- 内部字符串值解析函数 local function parse_str_val(str, pos, val) val = val or '' local early_end_error = 'End of input found while parsing string.' if pos > #str then error(early_end_error) end local c = str:sub(pos, pos) if c == '"' then return val, pos + 1 end if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'} local nextc = str:sub(pos + 1, pos + 1) if not nextc then error(early_end_error) end if nextc == 'u' then local hex_num = tonumber(str:sub(pos + 2, pos + 5), 16) if not hex_num then error('Invalid unicode escape at position ' .. pos) end local utf8_char if hex_num < 0x80 then utf8_char = string.char(hex_num) elseif hex_num < 0x800 then utf8_char = string.char(0xC0 + math.floor(hex_num / 0x40), 0x80 + (hex_num % 0x40)) else utf8_char = string.char(0xE0 + math.floor(hex_num / 0x1000), 0x80 + math.floor((hex_num % 0x1000) / 0x40), 0x80 + (hex_num % 0x40)) end return parse_str_val(str, pos + 6, val .. utf8_char) end return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc)) end -- 内部数值解析函数 local function parse_num_val(str, pos) local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos) local val = tonumber(num_str) if not val then error('Error parsing number at position ' .. pos .. '.') end return val, pos + #num_str end -- 公开序列化接口 function json.stringify(obj, as_key) local s = {} local kind = kind_of(obj) if kind == 'array' then if as_key then error('Cannot encode array as key.') end s[#s + 1] = '[' for i, val in ipairs(obj) do if i > 1 then s[#s + 1] = ',' end s[#s + 1] = json.stringify(val) end s[#s + 1] = ']' elseif kind == 'table' then if as_key then error('Cannot encode table as key.') end s[#s + 1] = '{' for k, v in pairs(obj) do if #s > 1 then s[#s + 1] = ',' end s[#s + 1] = json.stringify(k, true) s[#s + 1] = ':' s[#s + 1] = json.stringify(v) end s[#s + 1] = '}' elseif kind == 'string' then return '"' .. escape_str(obj) .. '"' elseif kind == 'number' then if as_key then return '"' .. tostring(obj) .. '"' end return tostring(obj) elseif kind == 'boolean' then return tostring(obj) elseif kind == 'nil' then return 'null' else error('Unjsonifiable type: ' .. kind .. '.') end return table.concat(s) end -- 公开反序列化接口 function json.parse(str, pos, end_delim) pos = pos or 1 if pos > #str then error('Reached unexpected end of input.') end local pos = pos + #str:match('^%s*', pos) local first = str:sub(pos, pos) if first == '{' then local obj, key, delim_found = {}, true, true pos = pos + 1 while true do key, pos = json.parse(str, pos, '}') if key == nil then return obj, pos end if not delim_found then error('Comma missing between object items at position ' .. pos) end pos = skip_delim(str, pos, ':', true) obj[key], pos = json.parse(str, pos) pos, delim_found = skip_delim(str, pos, ',') end elseif first == '[' then local arr, val, delim_found = {}, true, true pos = pos + 1 while true do val, pos = json.parse(str, pos, ']') if val == nil then return arr, pos end if not delim_found then error('Comma missing between array items at position ' .. pos) end arr[#arr + 1] = val pos, delim_found = skip_delim(str, pos, ',') end elseif first == '"' then return parse_str_val(str, pos + 1) elseif first == '-' or first:match('%d') then return parse_num_val(str, pos) elseif first == end_delim then return nil, pos + 1 else local literals = {['true'] = true, ['false'] = false, ['null'] = nil} for lit_str, lit_val in pairs(literals) do local lit_end = pos + #lit_str - 1 if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end end local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10) error('Invalid json syntax starting at ' .. pos_info_str) end end -- 以下为文件读取逻辑 -- 目标JSON路径:Windows下要么用正斜杠,要么用双反斜杠转义,不要直接写单反斜杠 local target_json_path = "C:/Users/Lichr/Documents/test.json" -- 以二进制只读模式打开文件,避免Windows自动转换换行符 local file, err = io.open(target_json_path, "rb") if not file then error("文件打开失败: " .. err) end -- 读取全量文件内容 local file_content = file:read("*a") file:close() -- 去除Windows下UTF-8 BOM头(记事本等工具保存JSON默认带该头,会导致解析失败) if file_content:sub(1,3) == string.char(0xEF, 0xBB, 0xBF) then file_content = file_content:sub(4) end -- 转换为Lua表 local lua_table = json.parse(file_content) -- 打印结果验证 print("JSON解析成功,转换后的Lua表内容:") for k, v in pairs(lua_table) do print(string.format("[%s] = %s", tostring(k), tostring(v))) end
运行注意事项
- 替换代码中
target_json_path的值为你实际要读取的JSON文件完整路径,路径中禁止直接使用单反斜杠,统一替换为正斜杠/或者双反斜杠\\ - 如果运行时提示文件打开失败,先确认路径对应文件真实存在、当前Windows账号对目标目录有读取权限、文件没有被其他程序独占锁定
- 终端运行命令:进入代码文件所在目录后执行
lua 你的文件名.lua即可看到输出
内容的提问来源于stack exchange,提问作者Age
相关产品推荐
相关产品推荐

