转发请注明出处!
本文介绍c++解压常见的各类压缩文件包括rar,zip,7z,gz,bz2,tar,rpm,jar
因为第三方库的原因,使用到了unrar,libarchive,libz,bzip2等几个库,所以需要先安装这几个库
这里不做介绍
1.解压rar
使用unrar进行解压,unrar我是源码编译,将编译好的
libunrar.so放到自己的目录中去
#include "../lib/src/trans/unrar/rar1.hpp" //需要引入rar1.hpp这个头文件,路径换成自己的
//这个是我处理解压后文件存放路径的
string getdst_path(char *src_filepath, const string &dst_path)
{
char rar_filename[512] = { 0 };
char *e_pos = strrchr(src_filepath, '/');
if (e_pos != NULL) {
strcpy(rar_filename, e_pos + 1);
}
else {
strcpy(rar_filename, src_filepath);
}
char dst_filename[512] = { 0 };
sprintf(dst_filename, "%s%s_#$1", dst_path.c_str(), rar_filename);
printf("----------- 00 dst_filename = %s\n", dst_filename);
return dst_filename;
}
int decompression_rarfile(char *rar_filepath, const char *dst_path)
{
if (rar_filepath == NULL || dst_path == NULL) return -1;
RAROpenArchiveDataEx archiveData;
memset(&archiveData, 0, sizeof(archiveData));
archiveData.ArcName = rar_filepath;
archiveData.OpenMode = RAR_OM_EXTRACT;
HANDLE hArcData = RAROpenArchiveEx(&archiveData);
if (archiveData.OpenResult != 0)
{
return -1;
}
string dst_filename = getdst_path(rar_filepath, dst_path);
RARHeaderDataEx headerData;
memset(&headerData, 0, sizeof(headerData));
int result;
while ((result = RARReadHeaderEx(hArcData, &headerData)) == 0)
{
if (headerData.Flags & RHDF_DIRECTORY)
{
continue; // 跳过目录
}
if (RARProcessFile(hArcData, RAR_EXTRACT, (char*)dst_filename.c_str(), NULL) != 0)
{
return -1;
}
}
RARCloseArchive(hArcData);
return 0;
}
在makefile 文件中要加入-lunrar
2.zip,7z,tar,rpm,jar格式的解压
这几个格式采用了libarchive这个第三方库进行解压,这个库支持很多格式的解压
libarchive可以解压jar包是jar采用了和zip一样的压缩算法
下面是archive.h中所包含的所有支持的格式和算法
__LA_DECL int archive_read_support_filter_all(struct archive *);
__LA_DECL int archive_read_support_filter_bzip2(struct archive *);
__LA_DECL int archive_read_support_filter_compress(struct archive *);
__LA_DECL int archive_read_support_filter_gzip(struct archive *);
__LA_DECL int archive_read_support_filter_grzip(struct archive *);
__LA_DECL int archive_read_support_filter_lrzip(struct archive *);
__LA_DECL int archive_read_support_filter_lz4(struct archive *);
__LA_DECL int archive_read_support_filter_lzip(struct archive *);
__LA_DECL int archive_read_support_filter_lzma(struct archive *);
__LA_DECL int archive_read_support_filter_lzop(struct archive *);
__LA_DECL int archive_read_support_filter_none(struct archive *);
__LA_DECL int archive_read_support_filter_program(struct archive *,
const char *command);
__LA_DECL int archive_read_support_filter_program_signature
(struct archive *, const char * /* cmd */,
const void * /* match */, size_t);
__LA_DECL int archive_read_support_filter_rpm(struct archive *);
__LA_DECL int archive_read_support_filter_uu(struct archive *);
__LA_DECL int archive_read_support_filter_xz(struct archive *);
__LA_DECL int archive_read_support_filter_zstd(struct archive *);
__LA_DECL int archive_read_support_format_7zip(struct archive *);
__LA_DECL int archive_read_support_format_all(struct archive *);
__LA_DECL int archive_read_support_format_ar(struct archive *);
__LA_DECL int archive_read_support_format_by_code(struct archive *, int);
__LA_DECL int archive_read_support_format_cab(struct archive *);
__LA_DECL int archive_read_support_format_cpio(struct archive *);
__LA_DECL int archive_read_support_format_empty(struct archive *);
__LA_DECL int archive_read_support_format_gnutar(struct archive *);
__LA_DECL int archive_read_support_format_iso9660(struct archive *);
__LA_DECL int archive_read_support_format_lha(struct archive *);
__LA_DECL int archive_read_support_format_mtree(struct archive *);
__LA_DECL int archive_read_support_format_rar(struct archive *);
__LA_DECL int archive_read_support_format_rar5(struct archive *);
__LA_DECL int archive_read_support_format_raw(struct archive *);
__LA_DECL int archive_read_support_format_tar(struct archive *);
__LA_DECL int archive_read_support_format_warc(struct archive *);
__LA_DECL int archive_read_support_format_xar(struct archive *);
/* archive_read_support_format_zip() enables both streamable and seekable
* zip readers. */
__LA_DECL int archive_read_support_format_zip(struct archive *);
如果只需要解压一种或者几种格式的,可以使用格式对应的具体函数,否则就可以用
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
解压代码:
#include <archive.h>
#include <archive_entry.h>
//上面这两个头文件需要引入
int decompression_tar_zip_7z_file(char *input_file_path, const char *dst_path, int file_type, int file_size)
{
struct archive* a;
struct archive_entry* entry;
a = archive_read_new();
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
int r = archive_read_open_filename(a, input_file_path, file_size);
if ((r != ARCHIVE_OK)) {
return r;
}
string dst_filename = getdst_path(input_file_path, dst_path);
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
//获取压缩文件条目的路径。
const char* entryPath = archive_entry_pathname(entry);
std::string destPath = dst_filename + "/" + entryPath;
//设置解压缩后的文件路径。
archive_entry_set_pathname(entry, destPath.c_str());
r = archive_read_extract(a, entry, ARCHIVE_EXTRACT_TIME);
if ((r != ARCHIVE_OK)) {
return r;
}
}
archive_read_close(a);
archive_read_free(a);
return 0;
}
makefile 加入 -larchive
3.解压gz格式
gz格式只支持单个文件的压缩,对于gz格式,我尝试了在linux下的解压,发现不能破坏其默认打包名字,重命名会破坏原有文件名称,例如我的文件名称是001.txt.gz,我重名为001.gz,那么用命令解压得到的文件名称就是001,没有txt的后缀。但是在windows下,用7z工具打开文件,却能看到子文件的真实名称,这里后面在研究下。
#include <zlib.h>
#include <boost/filesystem.hpp>
//需要引入这两个
int decompress_GzipFile(const std::string& gzipFilePath , const std::string& extractPath,bool first) {
gzFile gzFile = gzopen(gzipFilePath.c_str(), "rb");
if (gzFile == nullptr) {
return -1;
}
string dst_path = getdst_path((char*)gzipFilePath.c_str(),extractPath);
if (access(dst_path.c_str(), F_OK))
{
if (!boost::filesystem::create_directories(dst_path))
{
return -1;
}
}
// 获取压缩文件的名称
int pos = -1;
string src_tarfileName;
if(first)
{
//$1 是文件路径中我自己添加的路径分割标识
pos = gzipFilePath.rfind("$1");
src_tarfileName = gzipFilePath.substr(pos+4,gzipFilePath.length()-pos -5); //-7是去除$1和.gz扩展名
}
else
{
pos = gzipFilePath.rfind("/");
src_tarfileName = gzipFilePath.substr(pos,gzipFilePath.length()-pos -1); //-1是去除$1和.gz扩展名
}
// cout<<"------ src_tarfileName = "<<src_tarfileName<<","<<src_tarfileName.length()<<endl;
// 循环解压缩每个文件
char buffer[16384];
int uncompressedBytes;
std::string currentFileName;
while ((uncompressedBytes = gzread(gzFile, buffer, sizeof(buffer))) > 0) {
// 如果还没有设置当前文件的名称,则读取压缩文件的原始名称
if (currentFileName.empty()) {
currentFileName = dst_path + "/" + src_tarfileName;
}
// cout<< "------currentFileName = "<<currentFileName<<endl;
// 解压缩数据写入目标文件
std::ofstream outFile(currentFileName, std::ios::binary | std::ios::app);
if (!outFile) {
gzclose(gzFile);
return -1;
}
outFile.write(buffer, uncompressedBytes);
outFile.close();
// 如果已经读取完一个文件,则重置当前文件名称用于解压缩下一个文件
if (gzeof(gzFile)) {
currentFileName.clear();
}
}
// 关闭压缩文件
gzclose(gzFile);
return 0;
}
makefile 添加-lz
4.解压bz2格式
bz2格式和gz格式一样,只支持单个文件的压缩,重命名会破坏原有文件名称
#include <cerrno>
#include <boost/filesystem.hpp>
#include "../include/bzlib.h" //bzip2是编译的源码,这里是我把bzlib.h添加到了一个路径中,你要替换成自己的
int decompressBz2File2(const std::string& inputFilePath, const std::string& outputDirectory) {
FILE* inputFile = fopen(inputFilePath.c_str(), "rb");
if (!inputFile) {
std::cerr << "Failed to open input file: " << inputFilePath << std::endl;
return -1;
}
BZFILE* bzip2File = BZ2_bzReadOpen(nullptr, inputFile, 0, 0, nullptr, 0);
if (bzip2File == nullptr) {
std::cerr << "Failed to open bzip2 file: " << inputFilePath << std::endl;
fclose(inputFile);
return -1;
}
// 获取压缩文件的名称
int pos = -1;
string src_fileName;
if(true)
{
pos = inputFilePath.rfind("$1");
src_fileName = inputFilePath.substr(pos+4,inputFilePath.length()-pos -6);
}
else
{
pos = inputFilePath.rfind("/");
src_fileName = inputFilePath.substr(pos,inputFilePath.length()-pos -2);
}
cout<<"------ src_tarfileName = "<<src_fileName<<","<<src_fileName.length()<<endl;
std::string outputFileDir = getdst_path((char*)inputFilePath.c_str(),outputDirectory);
if (access(outputFileDir.c_str(), F_OK))
{
if (!boost::filesystem::create_directories(outputFileDir))
{
printf("---- create target path fail, %s, error = %s \n", outputFileDir.c_str(), strerror(errno));
return -1;
}
}
std::string outputFilePath = outputFileDir + "/" + src_fileName;
// 创建目标文件夹(如果不存在)
cout<< "------ outputFilePath = "<<outputFilePath<<endl;
char buffer[4096];
std::ofstream outputFileStream;
outputFileStream.open(outputFilePath, std::ios::out | std::ios::binary);
if (!outputFileStream.is_open()) {
std::cerr << "Failed to open output file,error =" << strerror(errno) << std::endl;
BZ2_bzReadClose(nullptr, bzip2File);
fclose(inputFile);
return -1;
}
int bzerror, bytesRead;
/*从压缩文件b读取最多len(未压缩)字节到缓冲区buf。如果读取成功,则
bzerror设置为BZ_OK,并返回读取的字节数。如果检测到逻辑流结
束,bzerror将被设置为BZ_STREAM_END,并返回读取的字节数。所有其
他bzerror值表示错误。
*/
while ((bytesRead = BZ2_bzRead(&bzerror, bzip2File, buffer, 4096)) > 0) {
outputFileStream.write(buffer, bytesRead);
}
BZ2_bzReadClose(nullptr, bzip2File);
fclose(inputFile);
outputFileStream.close();
return 0;
}
makefile 要加入-lbz2
参考文档【从0到1了解Libarchive】Libarchive的用途意义以及成功入门Libarchive_libarchive编译-CSDN博客