需求:遍历一个文件夹,得到一个 json 对象,键是文件名,值是文件夹内各文件的信息。

问题

问题:代码明明写得没问题,但是运行时却报错。

初始代码如下。这里为了简化问题,将文件的路径作为 json 对象键值对的值。

main.cpp

#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
#include <filesystem>
 
using json = nlohmann::json;
namespace fs = std::filesystem;
 
int main() {
	json j;
 
	fs::path dir_path{R"(C:\Users\Aoyu\Desktop\testA)"};
	for (const auto& entry : fs::recursive_directory_iterator(dir_path)) {
		auto relative_path = fs::relative(entry.path(), dir_path);
		j[relative_path.filename().string()] = relative_path.string();
	}
 
	std::cout << j.dump(4) << std::endl;
 
    return 0;
}

CMake 配置文件CMakeLists.txt:

cmake_minimum_required(VERSION 3.25)

# 设置 vcpkg 工具链文件(如果没有通过命令行指定)
# 如果在 CMakeList.txt 文件中设置 CMAKE_TOOLCHAIN_FILE,请确保在调用 project() 之前设置变量。
set(CMAKE_TOOLCHAIN_FILE "C:/Users/Aoyu/.vcpkg-clion/vcpkg/scripts/buildsystems/vcpkg.cmake")

project(Test)

set(CMAKE_CXX_STANDARD 23)

find_package(nlohmann_json CONFIG REQUIRED)

add_executable(Test main.cpp)
target_link_libraries(Test PRIVATE nlohmann_json::nlohmann_json)

运行报错:

Debug Error!

Program: D:\ClionProjects\Test2\cmake-build-debug\Test.exe

abort() has been called

(Press Retry to debug the application)

调试后发现是j.dump(4)抛出的异常。在代码中捕获异常,异常的描述信息:

[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xB9

后面经过不断测试,确定是文件路径中存在中文字符导致的。但是这只是表层原因,真正的原因是,程序不是以 UTF8 编码处理中文

解决办法

如果在 Windows 平台下使用 MSVC 编译器编译程序,让编译器以 UTF-8 模式编译源代码,确保源代码文件中的中文字符能被正确解析。

CMakeLists.txt

# ...

if(WIN32)
    # 添加 UTF-8 编码支持
    add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
    add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
endif()

project(Test)

# ...

在程序的 main() 函数开头,添加:

main.cpp

#ifdef _WIN32
    // 设置控制台输出代码页为UTF-8
    SetConsoleOutputCP(CP_UTF8);
    // 设置控制台输入代码页为UTF-8
    SetConsoleCP(CP_UTF8);
#endif
    // 程序本地化设置
    std::setlocale(LC_ALL, ".UTF-8");
    std::locale::global(std::locale(".UTF-8"));

下面是同样的代码,额外加上了更多注释。

main.cpp

#ifdef _WIN32
    // 设置控制台输出代码页为UTF-8
    // 确保`std::cout`等输出流能正确显示UTF-8编码的文本
    SetConsoleOutputCP(CP_UTF8);
    // 设置控制台输入代码页为UTF-8
    // 确保`std::cin`等输入流能正确读取UTF-8编码的文本
    SetConsoleCP(CP_UTF8);
#endif
    // 程序本地化设置(区域设置)
    // C 风格的本地化设置
    // `LC_ALL`:影响所有本地化类别,包括:数字格式、货币格式、日期和时间格式、字符分类和转换、字符串排序规则
    // `.UTF-8`:指定使用系统当前语言的UTF-8编码版本
    std::setlocale(LC_ALL, ".UTF-8");
    //  C++ 风格的本地化设置
    // 设置程序的全局locale,确保C++标准库的各种操作使用正确的本地化设置
    std::locale::global(std::locale(".UTF-8"));

在这个程序中,真正起作用的是CMakeLists.txt中的配置和这句代码std::locale::global(std::locale(".UTF-8"));

其他的代码加上也不冗余,可以让程序更稳健。比如这句代码std::setlocale(LC_ALL, ".UTF-8");会在使用 C 第三方库时发挥作用,如这里cpp.q2k1k.24.1121.0121

演示

程序现在可以正确处理文件路径中的中文了,例如:

{
    "8.png": "haha\\8.png",
    "haha": "haha",
    "t.cpp": "t.cpp",
    "t.o": "t.o",
    "t5.tex": "t5.tex",
    "t6.cpp": "t6.cpp",
    "你好": "你好",
    "哈哈哈.txt": "你好\\哈哈哈.txt"
}

补充

注意到 Windows 下路径的分隔符是\\,能否改为使用/呢?

relative_path.string()改为relative_path.generic_string()即可:

{
    "8.png": "haha/8.png",
    "haha": "haha",
    "t.cpp": "t.cpp",
    "t.o": "t.o",
    "t5.tex": "t5.tex",
    "t6.cpp": "t6.cpp",
    "你好": "你好",
    "哈哈哈.txt": "你好/哈哈哈.txt"
}

完整代码

完整代码见:nlohmann::json序列化中文乱码问题24.1124.2159