使用g++ 12.1.0编译含std::filesystem的代码时出现“Cannot convert character sequence: Invalid or incomplete multibyte or wide character”错误
我前阵子刚踩过这个一模一样的坑!把g从8.2.x升级到12.1.0后,Linux下处理带UTF-8字符的文件名直接报这个错,可Windows用VS2022、Linux用旧版g都跑得好好的,一开始我还以为是代码哪里写错了,折腾半天才搞明白是新版g++的std::filesystem编码处理变严格了。
问题根源
g++ 10之后的版本对C标准的遵循更严谨,Linux平台下的std::filesystem会严格按照程序运行时的**区域设置(locale)**来处理字符编码。旧版g(比如8.x)的实现比较“宽松”,直接把UTF-8的窄字符路径传给系统调用,而Linux内核本身允许任意字节序列当文件名,所以没触发错误。但新版g++会根据当前locale验证字符编码——如果程序默认用的是C locale(POSIX默认locale,完全不支持UTF-8),那带UTF-8的文件名就会被判定为无效的多字节字符,直接弹出这个报错。
至于Windows平台没问题,是因为VS的std::filesystem底层用的是UTF-16接口,会自动处理UTF-8到UTF-16的转换,完全不受locale限制。
解决方案
最稳妥的办法是在程序启动时显式设置支持UTF-8的locale,代码里加这么一段就行:
#include <locale> int main() { // 优先加载系统默认的UTF-8 locale,适配不同发行版 std::locale::global(std::locale("")); // 如果上面的方式在某些特殊系统不生效,可以直接指定具体UTF-8 locale: // std::locale::global(std::locale("en_US.UTF-8")); // 接下来再执行你的std::filesystem操作 // ... 比如文件创建、复制的业务代码 ... }
解释一下:std::locale("")会自动加载系统当前的用户locale,现在大部分Linux发行版默认都是UTF-8 locale,所以能完美解析带特殊字符的文件名。如果怕系统locale不统一,直接写死"en_US.UTF-8"也可以,只要目标系统安装了这个locale就行。
要是你不想改代码,也可以在运行程序前临时设置环境变量救急:
export LC_ALL=en_US.UTF-8 ./your_compiled_program
不过这种方式依赖运行环境,不如在代码里显式设置靠谱。
额外提醒
编译的时候保持-std=c++17或更高版本就行,g12默认已经支持C17了,不需要额外加其他编译参数,核心问题就是运行时的locale设置。
内容来源于stack exchange

