Qt 压缩/解压文件

news2024/12/25 3:01:26

前面讲了很多Qt的文件操作,文件操作自然就包括压缩与解压缩文件了,正好最近项目里要用到压缩以及解压缩文件,所以就研究了一下Qt如何压缩与解压缩文件。

QZipReader/QZipWriter

QZipReader 和 QZipWriter 类提供了用于读取和写入 ZIP 格式文件的功能,这个是qt自带的,试了一下个人感觉并不好用。

首先QZipReader/QZipWriter是在Qt5.14之后才引入的,而且使用需要用到gui-private这个模块,搜了一下,在 Qt 中,gui-private 模块是 Qt 框架中的一个私有模块,通常不是为公共使用而设计的。该模块包含了一些 Qt GUI 框架内部使用的实现细节、私有接口和工具类,而不是为外部开发者提供的公共 API。一般来说,Qt 将其功能模块划分为公共模块和私有模块。公共模块包含了开发者可以直接使用的公共 API,而私有模块则包含了框架内部的实现细节和工具类,这些类通常不应该被外部开发者直接使用。所以才不推荐使用。直接看代码:

首先pro文件包含对应模块:

QT +=gui-private

头文件包含:

#include <QtGui/private/qzipreader_p.h>
#include <QtGui/private/qzipwriter_p.h>

压缩函数:

/**
 * @brief 压缩文件
 * @param fileNames 需要压缩的文件绝对路径
 * @param path 压缩后的文件的绝对路径
 */
void zip_compress(const QStringList &fileNames, const QString &path) {
  QZipWriter *zipWriter = new QZipWriter(path);
  foreach (const QString &fileName, fileNames) {
    if (fileName.isEmpty()) continue;
    QFileInfo fileInfo(fileName);
    if (!fileInfo.exists()) continue;
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) continue;
    zipWriter->addFile(fileInfo.fileName(), file.readAll());
    file.close();
  }
  zipWriter->close();
  if (zipWriter) {
    delete zipWriter;
    zipWriter = nullptr;
  }
}

解压函数:

/**
 * @brief 解压缩文件
 * @param fileName 需要解压缩的文件
 * @param path 解压后文件存放的目录
 */
void zip_decompress(const QString &fileName, const QString &path) {
  if (fileName.isEmpty()) {
    return;
  }
  QFileInfo fileInfo(fileName);
  if (!fileInfo.exists()) {
    return;
  }
  QZipReader *reader = new QZipReader(fileName);
  foreach (const QZipReader::FileInfo &info, reader->fileInfoList()) {
    if (!info.isDir) {
      QString filePath =
          QDir::cleanPath(path + QDir::separator() + info.filePath);
      QFile file(filePath);
      if (!file.open(QIODevice::WriteOnly)) {
        continue;
      }
      file.write(reader->fileData(info.filePath));
      file.close();
    } else {
      QString dirName =
          QDir::cleanPath(path + QDir::separator() + info.filePath);
      QDir dir(dirName);
      if (!dir.exists()) dir.mkdir(dirName);
    }
  }
  reader->close();
  if (reader) {
    delete reader;
    reader = nullptr;
  }
}

7z

常用的解压软件比较多,例如winrar、7z、bandzip等等,其中7z是支持命令行形式的,详细命令可以看一下这两篇文章:

7z 命令行详解_7z命令-CSDN博客

前面我的这篇文章:Qt外部调用进程类QProcess的使用_windows qt 调用其它进程-CSDN博客 

就介绍过Qt如何通过QProcess调用外部进程,所以可以先安装7z然后使用QProcess调用7z应用程序,通过传参形式来压缩和解压缩文件,直接看代码:

压缩函数:

/**
 * @brief z7_compress 压缩函数
 * @param fileNames 需要压缩的文件的绝对路径
 * @param path 压缩后生成的压缩文件的绝对路径
 */
void z7_compress(const QStringList &fileNames, const QString &path) {
    QProcess process(0);
    QStringList args;
    args.append("a");
    args.append(path);
    args.append(fileNames);
    args.append("-mx=3");
    process.start("7z.exe", args);//注意7z.exe的路径
    process.waitForStarted();
    process.waitForFinished();
}

解压缩函数: 

/**
 * @brief z7_decompress 解压缩函数
 * @param fileName 需要解压缩的压缩包路径
 * @param path 解压缩后生成的目录
 */
void z7_decompress(const QString &fileName, const QString &path) {
    QProcess process(0);
    QStringList args;
    args.append("x");
    args.append(fileName);
    args.append("-o" + path);
    args.append("-aoa");
    process.start("7z.exe", args);//注意7z.exe的路径
    process.waitForStarted();
    process.waitForFinished();
}

ZLib

Qt压缩和解压缩文件也可以借助第三方库,zlib或者libzip,这里只介绍zlib。libzip有兴趣的也可以自己下去研究。

zlib 是一个开源的数据压缩库,广泛用于许多应用程序和系统中。以下是关于 zlib 的详细介绍:

  1. 功能

    • zlib 库提供了数据压缩和解压缩的功能,支持使用 DEFLATE 算法进行数据压缩。
    • zlib 提供了简单的 API,易于集成到各种应用程序中。
    • zlib 还支持在压缩数据中添加校验和,以确保数据的完整性。
  2. 特点

    • 高效性:zlib 使用 DEFLATE 算法进行数据压缩,具有较高的压缩比和压缩速度。
    • 跨平台性:zlib 可以在各种操作系统上运行,包括 Windows、Linux、macOS 等。
    • 开源性:zlib 是一个开源库,使用 zlib 的应用程序可以遵循 zlib 的开源许可协议。
  3. 使用场景

    • zlib 可用于在应用程序中对数据进行压缩,减小数据传输和存储的大小。
    • zlib 还可用于处理压缩文件格式,如 ZIP、PNG、HTTP 的 gzip 压缩等。
    • zlib 也常用于实时数据流的压缩,如网络传输中的数据压缩。
  4. API

    • zlib 提供了一组简单的 C 函数来进行数据压缩和解压缩,如 compress()uncompress()deflate()inflate() 等。
    • zlib 还提供了一些工具函数和数据结构,如 z_stream 结构体用于维护压缩和解压缩的状态。

首先需要下载zlib源码,zlib官网,下载之后解压然后准备编译:

使用Cmake:

 注意cmake对应vs,我电脑vs装的是VS2022,所以我这里选的是2022。

然后先Configure再Generate等待完成:

然后进入生成目录使用VS打开对应工程文件:

编译生成:

 注意debug和relase版本以及平台。

对应生成的库文件以及导入库文件:

 建立这样的一个文件目录结构,然后准备在代码中使用:

注意:zconf.h这个文件在cmake生成的文件目录里面 。dll动态库文件放在可执行文件同级目录下。

在工程文件中引入对应头文件以及库文件:

#引入zlib头文件和库
INCLUDEPATH += $PWD/zlib/include

win32 {
    LIBS += -L$$PWD/zlib/lib
    CONFIG(release, debug|release) {
        LIBS += -lzlib
    }
    CONFIG(debug, debug|release) {
        LIBS += -lzlibd
    }
}

试一下zlib的压缩以及解压字符串函数。

压缩字符串:

static QByteArray compress(const QByteArray &data) {
    z_stream zs;
    memset(&zs, 0, sizeof(zs));

    if (deflateInit(&zs, Z_BEST_COMPRESSION) != Z_OK) { return QByteArray(); }

    zs.next_in = (Bytef *)data.constData();
    zs.avail_in = data.size();

    int ret;
    char outbuffer[32768];
    QByteArray outstring;

    do {
        zs.next_out = reinterpret_cast<Bytef *>(outbuffer);
        zs.avail_out = sizeof(outbuffer);

        ret = deflate(&zs, Z_FINISH);

        if (outstring.size() < zs.total_out) {
            outstring.append(outbuffer, zs.total_out - outstring.size());
        }
    } while (ret == Z_OK);

    deflateEnd(&zs);

    if (ret != Z_STREAM_END) { return QByteArray(); }

    return outstring;
}


解压缩字符串:

static QByteArray decompress(const QByteArray &data) {
    z_stream zs;
    memset(&zs, 0, sizeof(zs));

    if (inflateInit(&zs) != Z_OK) { return QByteArray(); }

    zs.next_in = (Bytef *)data.constData();
    zs.avail_in = data.size();

    int ret;
    char outbuffer[32768];
    QByteArray outstring;

    do {
        zs.next_out = reinterpret_cast<Bytef *>(outbuffer);
        zs.avail_out = sizeof(outbuffer);

        ret = inflate(&zs, 0);

        if (outstring.size() < zs.total_out) {
            outstring.append(outbuffer, zs.total_out - outstring.size());
        }
    } while (ret == Z_OK);

    inflateEnd(&zs);

    if (ret != Z_STREAM_END) { return QByteArray(); }

    return outstring;
}

测试压缩一个长字符串:

zlib的压缩与解压缩文件目前我网上搜的只能压缩和解压缩单个的,多个的目前没有找到,简单看一下压缩和解压缩的函数。

bool compress(const QString &fileName, const QString &zipFileName) {
    QFile inputFile(fileName);
    if (!inputFile.open(QIODevice::ReadOnly)) { return false; }

    gzFile zipFile = gzdopen(inputFile.handle(), "wb");
    if (!zipFile) { return false; }

    char buffer[1024];
    int bytesRead;
    while ((bytesRead = inputFile.read(buffer, sizeof(buffer))) > 0) {
        gzwrite(zipFile, buffer, bytesRead);
    }

    gzclose(zipFile);  // 关闭文件句柄
    inputFile.close(); // 关闭输入文件

    return true;
}

bool decompress(const QString &zipFileName, const QString &fileName) {
    QFile inputFile(zipFileName);
    if (!inputFile.open(QIODevice::ReadOnly)) { return false; }

    QFile outputFile(fileName);
    if (!outputFile.open(QIODevice::WriteOnly)) { return false; }

    gzFile zipFile = gzdopen(inputFile.handle(), "rb");
    if (!zipFile) { return false; }

    char buffer[1024];
    int bytesRead;
    while ((bytesRead = gzread(zipFile, buffer, sizeof(buffer))) > 0) {
        if (outputFile.write(buffer, bytesRead) != bytesRead) { return false; }
    }

    gzclose(zipFile);
    inputFile.close();
    outputFile.close();

    return true;
}

那如果要压缩多个文件怎么办?这里可以使用QuaZip。

QuaZip

QuaZIP 是一个用于 Qt 框架的 C++ 库,它提供了对 ZIP 归档文件的读写功能。QuaZIP 是基于 zlib 和 minizip 库的封装,使得在 Qt 项目中可以方便地处理 ZIP 文件。也就是说QuaZIP是基于zlib的,使用QuaZIP的前提是已经编译好了zlib,zlib的编译前面已经讲了。这里直接开始讲QuaZIP的编译与使用。

首先下载QuaZIP,QuaZIP官网。下载之后进行解压:

可以看到里面有pro文件也有vs工程文件也有CMakeLists.txt文件,我们直接使用QtCreator来进行编译。首先因为QuaZIP依赖zlib,所以把刚刚编译的zlib文件放进来:

这个文件夹结构跟刚刚一样。

我们只编译库,所以进到这个目录:

 

把对应头文件放进来:

对应工程文件添加引入zlib:

然后开始编译生成对应debug和release版的库文件和导入库文件:

同样方法创建一个quazip文件夹,然后拷贝对应头文件以及导入库文件:

新建一个工程,将对应quazip文件放进工程文件夹内容,然后对应pro文件引入quazip头文件以及相关导入库文件:

INCLUDEPATH += $PWD/quazip/include

win32 {
    LIBS += -L$$PWD/quazip/lib
    CONFIG(release, debug|release) {
        LIBS += -lquazip
    }
    CONFIG(debug, debug|release) {
        LIBS += -lquazipd
    }
}

对应动态库文件放入到可执行文件同级目录下:

注意:别忘了zlib的库文件,我测试用的debug版,所以理论上只放debug版对应zlib和QuaZIP的库文件就够了。

QuaZIP压缩和解压缩文件已经封装好了方法,比如将指定目录内的所有子目录以及文件压缩到指定压缩包内方法:

  \param fileCompressed The name of the archive.
      \param dir The directory to compress.
      \param recursive Whether to pack the subdirectories as well, or
      just regular files.
      \return true if success, false otherwise.
      */
    static bool compressDir(QString fileCompressed, QString dir = QString(), bool recursive = true);

对应的解压方法:

/**
      \param fileCompressed The name of the archive.
      \param dir The directory to extract to, the current directory if
      left empty.
      \return The list of the full paths of the files extracted, empty on failure.
      */
    static QStringList extractDir(QString fileCompressed, QString dir = QString());
    /// Get the file list.

写一个简单的测试程序。

界面ui:

头文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
  Q_OBJECT

 public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow();

 private slots:
  void on_openDir_clicked();

  void on_compress_clicked();

  void on_selectZip_clicked();

  void on_decompress_clicked();

 private:
  Ui::MainWindow *ui;
};
#endif  // MAINWINDOW_H

源文件: 

#include "mainwindow.h"
#include "quazip/include/JlCompress.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QDir>
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);
}

MainWindow::~MainWindow() { delete ui; }

void MainWindow::on_openDir_clicked() {
  QString dirPath =
      QFileDialog::getExistingDirectory(nullptr, QString(), QString());
  ui->dePath->setText(dirPath);
}

void MainWindow::on_compress_clicked() {
  if (ui->dePath->text().isEmpty()) return;
  QDir deDir(ui->dePath->text());
  if (!deDir.exists()) return;
  if (deDir.isEmpty()) return;
  QString zipFileName = QDir::cleanPath(
      deDir.absolutePath() + QDir::separator() + deDir.dirName() + ".zip");
  bool r = JlCompress::compressDir(zipFileName, deDir.absolutePath());
  if (r)
    qDebug() << "compress success";
  else
    qDebug() << "compress fail";
}

void MainWindow::on_selectZip_clicked() {
  QString zipFileName = QFileDialog::getOpenFileName(
      nullptr, QString(), QString(), "Code Files (*.zip *.7z)");
  if (zipFileName.isEmpty()) return;
  ui->zipPath->setText(zipFileName);
}

void MainWindow::on_decompress_clicked() {
  if (ui->zipPath->text().isEmpty()) return;
  QString zipFileName = ui->zipPath->text();
  if (!QFile::exists(zipFileName)) return;
  QFileInfo zipFileInfo(zipFileName);
  QString deDirPath =
      QDir::cleanPath(zipFileInfo.absolutePath() + QDir::separator() +
                      zipFileInfo.completeBaseName());
  QDir deDir(deDirPath);
  if (!deDir.exists()) QDir().mkpath(deDirPath);
  QStringList fileList = JlCompress::extractDir(zipFileInfo.absoluteFilePath(),
                                                deDir.absolutePath());
  if (!fileList.isEmpty())
    qDebug() << "decompress success";
  else
    qDebug() << "decompress fail";
}

测试一下:

没有问题,这个测试文件的代码我会上传,仅供参考,我编译的zlib以及QuaZIP的库文件也在里面,建议自己编译,因为平台或者编译软件可能与我不同,我的环境是Windows,VS2022,Qt5.15.2。

其他相关压缩以及解压缩这里就不做介绍了,有兴趣的可以下去自己研究一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1549531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Healix Protocol 的 HLX 通证预售:医疗领域的未来展望

Healix Protocol推出 HLX 通证预售&#xff0c;将带来医疗领域的重要变革。通过其区块链技术&#xff0c;Healix Protocol致力于重新定义医疗服务的可及性与负担性&#xff0c;成为医疗行业的希望之光。该项目旨在增强透明度、可及性和效率&#xff0c;推动医疗体系向更加公平和…

Shut down, sleep, or hibernate your PC 关闭、睡眠或休眠

最近一段时间没有整服务器了~自己开始捉摸18年买的笔记本-x280&#xff0c;除了发现usb type c和thunderbolt 3接口的不一样外&#xff0c;也开始研究这个待机的功能了~找了官方文档&#xff0c;做个简易的翻译&#xff0c;给大家一起看看学习把。 关于Windows的 “睡眠” 和 …

【网络】:数据链路层

数据链路层 一.以太网帧格式二.认识MTU三.ARP协议1.ARP协议的工作流程2.ARP数据报的格式3.ARP欺骗 四.其他重要协议或技术1.DNS2.ICMP协议3.NAT技术&#xff08;重点&#xff09;4.代理服务器 五.一些问题 认识以太网 “以太网” 不是一种具体的网络, 而是一种技术标准; 既包含…

mysql 索引 B+tree

B树&#xff08;BTree&#xff09;&#xff1a; 结构&#xff1a; B树是一种自平衡的多路查找树&#xff0c;每个节点可以拥有M个子节点&#xff08;M通常是一个较大的常数&#xff0c;比如几百&#xff09;&#xff0c;每个节点最多含有M-1个键和M个指向子节点的指针。叶子节点…

C++类模板详解

在学习类模板之前可以了解一下函数模板&#xff0c;可以参考我的另一篇文章C函数模板详解&#xff08;结合代码&#xff09;-CSDN博客 讲解的比较详细&#xff0c;有助于理解类模板。 目录 1、什么是类模板&#xff1f; 2、类模板与函数模板区别 3、类模板对象做函数参数 …

《早起的奇迹》要么躺在床上等待生活的暴击,要么早起创造奇迹 - 三余书屋 3ysw.net

精读文稿 今天我们讲述的是关于《早起的奇迹》。通过神奇的早起&#xff0c;我们可以获得改变人生的力量。首先我要给你讲一个故事&#xff0c;这个故事的主人公是一个年轻人&#xff0c;他在少年时就取得了巨大的成就&#xff0c;在15岁时他已经拥有了自己的电台节目&#xff…

Failed to load plugin because libnvinfer.so.8: 无法打开共享目标文件: 没有那个文件或目录

首先查看自己是否安装了tensorrt&#xff08;如果没有&#xff0c;就先执行下面安装&#xff09; pip install tensorrt控制台输入如下代码&#xff0c;查看tensorrt包的路径 python3 -c "import tensorrt; print(tensorrt.__path__)"找到自己安装包的对应路径 3.…

鸿蒙OS开发实例:【工具类封装-http请求】

import http from ohos.net.http; import promptAction from ohos.promptAction; 封装HTTP接口请求类&#xff0c;提供格式化的响应信息输出功能。 使用 DevEco Studio 3.1.1 Release 及以上版本&#xff0c;API 版本为 api 9 及以上。 示例&#xff1a; import { MyHttpUtil…

centos安装jdk的坑

文章目录 一、安装jdk二、查找jdk的目录三、配置JAVA_HOME 一、安装jdk 我们一般用yum search java | grep jdk查询可以安装的jdk 但是一定要注意如下图&#xff0c;必须知道jdk和jre的区别 yum install java-1.8.0-openjdk-devel.x86_64二、查找jdk的目录 用如下命令 sudo…

kubernetes负载均衡资源-Ingress

一、Ingress概念 1.1 Ingress概念 使用NodePort类型的Service可以将集群内部服务暴露给集群外部客广端,但使用这种类型Service存在如下几个问题。 1、一个端口只能一个服务使用,所有通过NodePort暴露的端口都需要提前规划;2、如果通过NodePort暴露端口过多,后期维护成本太…

快讯|Tubi 在美国电视总观看的份额提升至 1.7%

​在每月一期的 Tubi 快讯中&#xff0c;你将全面及时地获取 Tubi 最新发展动态&#xff0c;欢迎&#x1f31f;星标关注【比图科技】&#xff0c;一起成长变强&#xff01; Tubi 在美国电视总观看的份额提升至 1.7% 根据 Nielsen 最新发布的 The Gauge 报告&#xff0c;2024 年…

监控系统介绍

文章目录 监控系统的分类日志类(logs)调用链类(tracing)度量类(metrics) 监控系统的分层监控系统典型架构采集器TelegrafExportersGrafana-Agent 时序库OpenTSDBInfluxDBTDEngineM3DBVictoriaMetricsTimescaleDBPrometheus 告警引擎数据展示 监控系统的分类 针对不同场景把监控…

网络: 传输层

功能: 将数据从发送到传给接收端 UDP 无连接状态: 知道对端的IP和端口号就直接进行传输, 不需要建立连接不可靠: 没有确认机制, 没有重传机制. 出错不会管面向数据包: 不能够灵活的控制读写数据的次数和数量 发送速度快: 立即发送 报文结构 TCP 面向连接可靠 校验和序列号(按…

基于SwinTransformer和Unet的自适应多类别分割

1、介绍 transformer的基于全局信息的把握&#xff0c;使得对图像的处理成为了可能 swin-transformer在图像分类应用的成功&#xff0c;证明了transformer比传统的cnn卷积网络可以更好的提取图像特征。因为图像分割也是基于特征信息的融合&#xff0c;将不同尺度下的信息拼接…

vivado 使用远程主机和计算群集

使用远程主机和计算群集 概述 AMD Vivado™集成设计环境&#xff08;IDE&#xff09;支持同时并行合成和实现的执行在多个Linux主机上运行。你可以做到这一点通过配置单个主机或指定要在其上启动作业的命令手动执行现有的计算集群。目前&#xff0c;Linux是Vivado唯一支持远程主…

负氧离子监测站:创造健康生活环境

TH-FZ5在蓝天白云之下&#xff0c;那一座座高耸的全彩屏负氧离子监测站&#xff0c;如同一支支科技的绿芽&#xff0c;静静破土而出&#xff0c;为这片土地带来了新的生命力。这些现代化的设备不仅美化了环境&#xff0c;更是我们呼吸健康守护者&#xff0c;它们的存在让我们的…

STM32F10X开发环境的搭建

一、keil软件安装 找到keil软件包&#xff0c;解压缩&#xff0c;找到keil5安装软件&#xff1a; 鼠标右键选择以管理员权限运行。点击next&#xff0c;直到安装结束。 安装完成后在桌面会出现keil5软件图标&#xff1a; 然后再安装相应的芯片支持包&#xff1a;我们用的是stm…

Leetcode - 2580. 统计将重叠区间合并成组的方案数

文章目录 思路AC CODE总结 题目链接&#xff1a;2580. 统计将重叠区间合并成组的方案数 思路 一个区间合并的板子&#xff0c;计算出区间数目之后&#xff0c;每个区间都有放左和放右两种选法&#xff0c;所以最后的答案就是 2 k 2^k 2k。但是需要用c进行二维数组的排序&…

【深度学习】深度学习md笔记总结第2篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…