需求:遍历一个文件夹,得到一个 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"
}