RAR5格式需依赖官方unrar源码解析,因标准C++库及libarchive均不支持;必须调用Archive::SetPassword设任意密码才能遍历目录,且需手动处理卷分割与树状结构构建。

RAR5格式不支持标准C++库直接解析
标准C++(包括std::filesystem、zlib绑定或libarchive默认配置)完全不识别RAR5文件结构。RAR5是专有二进制格式,未公开完整规范,且含自定义加密、分块校验和多段压缩流,libarchive 3.6+虽宣称“实验性支持”,但实际仅能解压已知密码的单文件,无法可靠遍历目录树。
必须依赖unrar源码或官方SDK
唯一稳定方案是集成unrar项目(RARLAB官方开源的unrar命令行工具源码)。注意:不是libunrar(第三方非官方封装),而是直接编译unrar/src下的archive.cpp、rarvm.cpp等核心模块。关键点:
-
Archive::Open返回ERAR_SUCCESS后,需调用Archive::GetNextHeader循环读取每项元数据,而非一次性加载全部 - 目录项(
FILE_ATTRIBUTE_DIRECTORY)与文件项共用同一结构体FileHeader,靠FileHeader::UnpSize == 0 && FileHeader::FileName[FileNameLength-1] == '/'粗略判断(RAR5无显式类型字段) - 路径分隔符恒为
'/',即使在Windows下打包,FileHeader::FileName也绝不会含'\'
绕过密码检查时的坑:不能跳过ProcessBlock
若RAR5文件带密码,但你只想读目录结构(不关心内容),仍必须提供任意非空密码调用Archive::SetPassword。否则GetNextHeader会在处理加密头时返回ERAR_MISSING_PASSWORD并终止。更隐蔽的问题是:ProcessBlock内部会校验AES-256密钥派生结果,若跳过该步骤(比如patch掉校验),后续GetNextHeader可能因CRC错位而解析出乱码文件名——因为RAR5的文件头紧跟在加密块之后,偏移依赖正确解密。
C++中构建轻量目录树的最小可行代码片段
以下代码假设已将unrar源码编译为静态库libunrar.a,且链接了-lz:
立即学习“C++免费学习笔记(深入)”;
#include "rar.hpp"
#include <vector>
#include <string>
<p>struct RarEntry {
std::string path;
bool is_dir;
};</p><p>std::vector<RarEntry> list_rar5(const char* filename) {
Archive arc;
if (arc.Open(filename) != ERAR_SUCCESS) return {};</p><pre class="brush:php;toolbar:false;">arc.SetPassword("dummy"); // 必须设,哪怕错
std::vector<RarEntry> entries;
while (true) {
FileHeader fh;
int res = arc.GetNextHeader(&fh);
if (res != ERAR_SUCCESS) break;
std::string name(fh.FileName, fh.FileNameLength);
// RAR5中目录名末尾带'/'; 但部分老工具打包可能省略,需结合UnpSize判断
bool is_dir = (name.back() == '/' || fh.UnpSize == 0);
if (is_dir && name.back() == '/') name.pop_back();
entries.push_back({name, is_dir});
}
return entries;}
这个函数能拿到扁平列表,但注意:RAR5不保证条目按路径层级排序,"a/b/"可能出现在"a/"之前,构建树状结构必须自行std::sort + std::string::starts_with做父子关系推导。
真正麻烦的不是解析,是当遇到带恢复记录(recovery record)或卷分割(vol001.rar / vol002.rar)的RAR5时,unrar SDK本身不自动合并卷——你得先手动拼出完整路径数组,再传给Archive::Open的重载版本,且顺序不能错。



















