You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向Lua脚本传递Unicode UTF-8格式的命令行参数?

Windows下Lua命令行参数UTF-8乱码解决方案

核心原因

标准Lua(5.4.8)的启动入口lua.c使用了ANSI版本的main函数,在Windows环境下,系统会将UTF-16编码的命令行参数转成系统默认ANSI代码页的字符串,非ASCII字符在此过程中会被损坏,导致你看到的????乱码。修改控制台代码页(chcp 65001)无法解决这个问题——因为数据在进入Lua之前就已经被转码破坏了。

简便解决方案:LuaJIT + FFI 直接调用Windows API

如果可以切换到LuaJIT,这是最不需要C语言开发经验的方案:利用LuaJIT的FFI特性直接调用Windows Unicode API,获取原始UTF-16命令行参数,再转换为UTF-8。

代码示例

local ffi = require("ffi")

-- 声明需要调用的Windows API
ffi.cdef[[
typedef wchar_t WCHAR;

WCHAR* GetCommandLineW(void);
int CommandLineToArgvW(WCHAR* lpCmdLine, int* pNumArgs);
void LocalFree(void* hMem);
int WideCharToMultiByte(unsigned int CodePage, unsigned long dwFlags, const WCHAR* lpWideCharStr, int cchWideChar, char* lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, int* lpUsedDefaultChar);
]]

local kernel32 = ffi.load("kernel32")
local crt = ffi.C

-- 获取UTF-8编码的命令行参数列表
local function get_utf8_argv()
    local cmdline = kernel32.GetCommandLineW()
    local argc = ffi.new("int[1]")
    local argv = kernel32.CommandLineToArgvW(cmdline, argc)
    if argv == nil then return {} end

    local args = {}
    for i = 0, argc[0]-1 do
        -- 将UTF-16转UTF-8
        local utf16_str = argv[i]
        local utf16_len = crt.wcslen(utf16_str)
        -- 先计算所需UTF-8缓冲区大小
        local utf8_len = crt.WideCharToMultiByte(65001, 0, utf16_str, utf16_len, nil, 0, nil, nil)
        local utf8_buf = ffi.new("char[?]", utf8_len + 1)
        crt.WideCharToMultiByte(65001, 0, utf16_str, utf16_len, utf8_buf, utf8_len, nil, nil)
        utf8_buf[utf8_len] = 0 -- 确保字符串以NULL结尾
        table.insert(args, ffi.string(utf8_buf))
    end

    kernel32.LocalFree(argv)
    table.remove(args, 1) -- 移除第一个元素(程序路径),和原生arg表对齐
    return args
end

-- 测试使用
local utf8_args = get_utf8_argv()
if utf8_args[1] then
    io.open("test.txt", "wb"):write(utf8_args[1])
end

使用方法

  1. 下载并使用LuaJIT替换标准Lua执行环境;
  2. 运行上述脚本,传入Unicode参数即可正确输出UTF-8内容。

标准Lua下的替代方案

如果必须使用标准Lua(非LuaJIT),可以选择:

  • 使用预编译扩展库:比如winapi库(提供了访问Windows Unicode API的Lua接口),直接调用其获取命令行参数的方法;
  • 轻量DLL实现:编写一个简单的C DLL,封装GetCommandLineW和CommandLineToArgvW逻辑,将UTF-16参数转成UTF-8后暴露给Lua。以下是极简DLL代码示例(需用MinGW或MSVC编译):
#include <lua.h>
#include <lauxlib.h>
#include <windows.h>

static int l_get_utf8_argv(lua_State *L) {
    LPWSTR cmdline = GetCommandLineW();
    int argc;
    LPWSTR *argv = CommandLineToArgvW(cmdline, &argc);
    if (!argv) return 0;

    lua_createtable(L, argc-1, 0);
    for (int i = 1; i < argc; i++) {
        int utf8_len = WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL);
        char *utf8_buf = (char*)malloc(utf8_len);
        WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, utf8_buf, utf8_len, NULL, NULL);
        lua_pushstring(L, utf8_buf);
        lua_rawseti(L, -2, i);
        free(utf8_buf);
    }
    LocalFree(argv);
    return 1;
}

int __declspec(dllexport) luaopen_winargv(lua_State *L) {
    lua_register(L, "get_utf8_argv", l_get_utf8_argv);
    return 0;
}

编译后在Lua中加载DLL即可调用get_utf8_argv()获取正确参数。

总结

  • 标准Lua因使用ANSI API导致命令行Unicode参数丢失,必须通过Unicode API直接获取原始数据;
  • LuaJIT + FFI是零编译成本的最优方案;
  • 标准Lua下可借助预编译扩展或简单DLL解决问题。

内容的提问来源于stack exchange,提问作者Badr Elmers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 22:13:17