Qt 压缩/解压文件

前面讲了很多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。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

思科网络中DHCP中继的配置

一、什么是DHCP中继&#xff1f;DHCP中继有什么用? &#xff08;1&#xff09;DHCP中继是指一种网络设备或服务&#xff0c;用于在不同的子网之间传递DHCP&#xff08;动态主机配置协议&#xff09;消息。DHCP中继的作用是帮助客户端设备获取IP地址和其他网络配置信息&#x…

边缘计算【智能+安全检测】系列教程-- Jeton Agx Orin 基础环境搭建

1 .前期准备 Jetson Agx Orin 比Jetson Agx Orin Xavier的算力要高&#xff0c;性能要好通常用来做自动驾驶的AI推理&#xff0c;具体外观如下图 1.刷机软件sdkmanager&#xff1a;下载链接 NVIDIA账号需要注册&#xff0c;正常一步一步往下走就行。在ubuntu18以上的系统安…

[iOS]GCD(一)

[iOS]GCD(一) 文章目录 [iOS]GCD(一)GCD的概要GCD的APIDispatch Queuedispatch_queue_createMain Dispatch_set_target_queuedispatch_afterDispatch Groupdispatch_barrier_asyncdispatch_applydispatch_applydispatch_suspend/dispatch_resumeDispatch Semaphoredispatch_onc…

LeetCode 面试经典150题 14.最长公共前缀

题目&#xff1a; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 思路&#xff1a; 代码&#xff1a; class Solution {public String longestCommonPrefix(String[] strs) {if (strs.length 0) {return &…

c语言 实现切片数组

文章目录 前言一、接口定义1、创建切片2、销毁切片3、添加元素4、切片长度5、切片容量 二、完整代码三、使用示例1、一般使用流程2、直接append3、自定义类型 总结 前言 由于c语言没有集合类的标准库&#xff0c;需要用时只能自己实现&#xff0c;由于c语言没有泛型&#xff0…

腾讯云GPU云服务器_GPU云计算_异构计算_弹性计算

腾讯云GPU服务器是提供GPU算力的弹性计算服务&#xff0c;腾讯云GPU服务器具有超强的并行计算能力&#xff0c;可用于深度学习训练、科学计算、图形图像处理、视频编解码等场景&#xff0c;腾讯云百科txybk.com整理腾讯云GPU服务器租用价格表、GPU实例优势、GPU解决方案、GPU软…

Android 项目新建问题总结

title: Android 项目新建问题总结 search: 2024-03-24 tags: “#Android 项目新建问题总结” Android 项目新建问题总结 一、gradle 项目每次都自动下载依赖包到C盘 背景&#xff1a;idea 首次打开一个 gradle 项目&#xff0c;都会在 C 盘下载项目所需的依赖包&#xff0c;但…

在fstab文件中配置UUID方式自动挂载数据盘、swap、目录(**)

linux如何挂在硬盘&#xff0c;自动挂载和手动挂载&#xff08;详细说明&#xff09;https://gitcode.csdn.net/65eedcea1a836825ed7a06f4.html 解决linux重启后磁盘挂载失效的问题 https://blog.csdn.net/sugarbliss/article/details/107033034 linux /etc/fstab 文件详细说…

服务消费微服务

文章目录 1.示意图2.环境搭建1.创建会员消费微服务模块2.删除不必要的两个文件3.检查父子模块的pom.xml文件1.子模块2.父模块 4.pom.xml 添加依赖&#xff08;刷新&#xff09;5.application.yml 配置监听端口和服务名6.com/sun/springcloud/MemberConsumerApplication.java 创…

【JavaEE初阶系列】——阻塞队列

目录 &#x1f6a9;阻塞队列的定义 &#x1f6a9;生产者消费者模型 &#x1f388;解耦性 &#x1f388;削峰填谷 &#x1f6a9;阻塞队列的实现 &#x1f4dd;基础的环形队列 &#x1f4dd;阻塞队列的形成 &#x1f4dd; 内存可见性 &#x1f4dd;阻塞队列代码 &#…

02-MySQL数据库的基本使用与密码设置

一、服务端口 3306端口和33060端口&#xff0c;是我们启动数据库后开启的监听端口&#xff1b; 3306端口&#xff1a;是我们MySQL服务的监听端口&#xff0c;用来连接数据库使用&#xff1b; 33060端口&#xff1a;MySQL-shell服务的端口&#xff0c;MySQL-shell是MySQL架构集群…

day3-QT

1>使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函。将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是…

DBA工作经验总结

目录 一、MySQL8.0创建一张规范的表 1.表、字段全采用小写 2.int类型不再加上最大显示宽度 3.每张表必须显式定义自增int类型的主键 4.建表时增加comment来描述字段和表的含义&#xff08;防止以后忘记&#xff09; 5.建议包含create_time和update_time字段 6.核心业务增…

FloodFill算法——力扣被围绕的区域

文章目录 题目解析算法解析代码解析 题目解析 被围绕的区域 我们来解读一下这个题目&#xff0c;这个题目的意思就是求出被X围绕的O有多少个&#xff0c;那么什么是被围绕呢&#xff1f;也就是没有出路并且连通的O不能到四条边上&#xff0c;这就算是被围绕了&#xff0c;可是…

oracle 19c RAC补丁升级

1.停止集群件备份家目录 ----两节点分别操作 cd /u01/app/19.3.0/grid/bin/ crsctl stop crstar -zcvf /u01/app.tar.gz /u01/app/u01/app/19.0.0/grid/bin/crsctl start crs2.两节点 GI、DB OPatch 替换&#xff08;都得执行&#xff09; ----# 表示 root 用户&#xff0c;$…

npm、nrm、nvm详解与应用

本文全面介绍了 npm、nrm 以及 nvm 这三个与 Node.js 开发密切相关的工具。首先&#xff0c;对 npm 进行了定义和功能解释&#xff0c;包括其在依赖管理、项目管理、脚本执行、版本控制和社区贡献等方面的作用。接着&#xff0c;详细介绍了 npm 的常用命令和设置下载源的操作&a…

SqlServer找不到SQL Server Configuration Manager(配置管理)

1、Win键 R &#xff0c;输入 compmgmt.msc 2、找到Sql Server配置管理器

iOS开发 - 转源码 - __weak问题解决

iOS开发 - 转源码 - __weak问题解决 在使用clang转换OC为C代码时&#xff0c;可能会遇到以下问题 cannot create __weak reference in file using manual reference 原因 __weak弱引用是需要runtime支持的&#xff0c;如果我们还只是使用静态编译&#xff0c;是无法正常转换的…

PCIe总线-PCIe总线简介(一)

1.概述 早期的计算机使用PCI&#xff08;Peripheral Component Interconnect&#xff09;总线与外围设备相连&#xff0c;PCI总线使用单端并行信号进行数据传输&#xff0c;由于单端信号很容易被外部系统干扰&#xff0c;其总线频率很难进一步提高。目前&#xff0c;为了提高总…

文件夹读取不到文件:深度解析与高效恢复策略

一、遭遇文件夹读取难题&#xff1a;文件离奇失踪 在日常使用电脑或移动设备的过程中&#xff0c;我们有时会遇到一个令人头疼的问题&#xff1a;原本存储着重要数据的文件夹突然变得“空空如也”&#xff0c;其中的文件仿佛凭空消失一般&#xff0c;无法正常读取。这种文件夹…