问题

我有一个需求:压缩指定的文件夹,解压缩指定的压缩包到指定位置。

我尝试了 libarchive、zlib、libzip、libzippp,无一例外,如果路径中有中文字符,全部失败。要么压缩包中的中文乱码但内容完整,要么压缩包就是坏的。

解决

这位网友提醒,对程序进行本地化设置后程序能够正确处理中文了。

关键部分:

CMakeLists.txt

if(WIN32)
    add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
    add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
endif()

解释:如果是 Windows 平台,且使用 MSVC 编译器,则添加 /utf-8 编译选项。作用:将源代码文件视为 UTF-8 编码,解决 MSVC 默认使用当前代码页的问题

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

main.cpp

int main() {
    std::setlocale(LC_ALL, "en_US.UTF-8");
    // ...
}

解释:对程序进行 C 风格的本地化设置。由于 libarchive 是 C 库,所以使用std::setlocale()进行本地化设置后 libarchive 就能使用 UTF-8 编码正确处理中文了。

如果是 C++ 程序还建议设置std::locale::global(std::locale(".UTF-8"));,这是 C++ 风格的本地化设置,只会影响程序中 C++ 代码的字符处理方式。

完整代码

完整代码见: 使用libarchive将文件夹压缩为.tar.gz文件后再次解压,解决中文路径乱码问题

补充

std::setlocale()基本语法

std::setlocale(
    LC_ALL,              // 类别参数
    "en_US.UTF-8"        // locale名称
);

类别参数选项:

LC_ALL          // 设置所有类别
LC_COLLATE      // 字符串比较和排序
LC_CTYPE        // 字符分类和转换
LC_MONETARY     // 货币格式化
LC_NUMERIC      // 数字格式化
LC_TIME         // 时间格式化
LC_MESSAGES     // 系统消息

常见的locale名称格式:

".UTF-8"         // UTF-8编码
"en_US.UTF-8"    // 美式英语,UTF-8编码
"zh_CN.UTF-8"    // 简体中文,UTF-8编码
"C"              // 最小 C locale
""               // 使用系统默认locale

编写一个程序检查当前 locale

#include <iostream>
#include <clocale>

void printCurrentLocale() {
    std::cout << "Current locale: " 
              << std::setlocale(LC_ALL, nullptr) 
              << std::endl;
}

int main() {
    printCurrentLocale();  // 检查初始locale
    std::setlocale(LC_ALL, "en_US.UTF-8");
    printCurrentLocale();  // 检查设置后的locale
}
Current locale: C
Current locale: en_US.UTF-8

std::locale::global()的使用效果

std::locale::global() 函数用于 C++ 风格的本地化设置(区域设置)。

例子:使用默认的区域设置输出一个数字,然后将全局区域设置更改为美国英语,并再次输出相同的数字。

#include <iostream>
#include <sstream>
 
void print() {
	std::stringstream stream;
	stream << 32767;
	std::cout << stream.str() << std::endl;
}
 
int main() {
	print();
 
	std::locale::global(std::locale{"en-US"});  // "en-US" 使用 Windows 的区域设置格式,类似 POSIX ,使用破折号`-`而非下划线`_`
 
	print();
}
32767
32,767