C语言实例_数据压缩与解压

news2024/9/23 16:23:54

一、压缩与解压介绍

数据压缩是通过一系列的算法和技术将原始数据转换为更紧凑的表示形式,以减少数据占用的存储空间。数据解压缩则是将压缩后的数据恢复到原始的表示形式。

数据可以被压缩打包并减少空间占用的原因有以下几个方面:

(1)无效数据的消除:在数据中可能存在大量冗余、重复或无效的信息。压缩算法可以通过识别和移除这些无效数据,从而减小数据的大小。

(2)统计特性的利用:数据通常具有某种统计特性,例如频繁出现的模式、重复的字节序列等。压缩算法可以利用这些统计特性来编码数据,从而达到更高的压缩比率。

(3)信息编码:压缩算法使用不同的编码方式来表示源数据,在保证数据可还原的前提下,使用更少的位数来表示信息。例如,Huffman编码、LZW编码等。

常见的应用场景中会使用到数据压缩和解压功能,例如:

(1)存储媒体:在硬盘、闪存等存储介质上,压缩可以节省存储空间,并提高存储效率。尤其在大规模的数据中心、云存储环境中,数据压缩可以显著减少存储成本。

(2)网络传输:在网络通信中,压缩可以减少数据传输的带宽消耗,提高传输速度。尤其在低带宽、高延迟的网络环境中,压缩可以显著改善传输性能。

(3)文件压缩:压缩工具如ZIP、RAR等常用于对文件进行打包和压缩,以减小文件的大小,便于存储和传输。这在文件传输、备份和归档中非常常见。

(4)多媒体编码:音频、图像、视频等多媒体数据往往具有较高的冗余性,压缩算法可以大幅减小文件大小,例如MP3、JPEG、H.264等压缩算法。

image-20230627233455020

二、ZIP格式介绍

ZIP是一种常见的文件压缩格式,它使用DEFLATE算法来进行数据压缩。

下面是ZIP压缩的基本原理:

(1)文件分块:ZIP压缩将要压缩的文件按照一定大小的块进行划分。每个块通常包含多个字节,并且可以独立地进行压缩处理。

(2)压缩算法:对于每个块,ZIP使用DEFLATE算法进行压缩。DEFLATE是一种无损的压缩算法,它结合了LZ77算法和霍夫曼编码,可以有效地消除冗余并提高压缩比率。

  • LZ77算法:遍历输入数据,寻找重复的模式(前缀)并使用指针来表示。通过将重复的模式替换为指针,可以达到数据压缩的效果。
  • 霍夫曼编码:利用字符出现的频率来设计一种更紧凑的编码方式。频率较高的字符使用较短的编码,频率较低的字符使用较长的编码。

(3)数据存储:压缩后的数据以块为单位存储在ZIP文件中。每个块都包含压缩后的数据、块的元数据和校验和等信息。

(4)全局文件目录:ZIP文件包含一个全局文件目录,记录了文件的结构以及每个文件的元数据。这使得ZIP文件能够存储多个文件,并确保可以正确地还原被压缩的文件。

  • 文件结构:全局文件目录记录了每个文件的名称、压缩前后的大小、压缩方法等信息。
  • 文件索引:全局文件目录还包含一个索引表,指明每个文件的起始位置和块的偏移量。通过索引表,可以快速定位并解压指定的文件块。

(5)压缩率:ZIP压缩的效果取决于输入文件的特性和DEFLATE算法的实现。通常情况下,文本文件和重复性较高的内容可以获得更高的压缩比率,而二进制文件和已经过压缩的文件(如JPEG图像)则可能无法再次获得显著的压缩。

ZIP压缩的好处是它广泛支持,并且可在各种操作系统和平台上使用。ZIP格式支持密码保护、文件夹结构、注释等功能,使其成为一种常用的压缩格式。

三、C语言实现压缩和解压算法

3.1 代码框架

下面是使用C语言实现压缩和解压的代码框架(下一章再实现完整的算法):

#include <stdio.h>
#include <stdlib.h>

void compressFile(const char* inputFile, const char* outputFile) {
    FILE* input = fopen(inputFile, "rb");
    FILE* output = fopen(outputFile, "wb");
    if (input == NULL || output == NULL) {
        printf("Failed to open files\n");
        return;
    }

    // 在这里执行压缩算法,将input文件的内容压缩,并写入output文件

    fclose(input);
    fclose(output);
}

void decompressFile(const char* compressedFile, const char* outputFile) {
    FILE* input = fopen(compressedFile, "rb");
    FILE* output = fopen(outputFile, "wb");
    if (input == NULL || output == NULL) {
        printf("Failed to open files\n");
        return;
    }

    // 在这里执行解压算法,将compressedFile文件的内容解压,并写入output文件

    fclose(input);
    fclose(output);
}

int main() {
    const char* inputFile = "input.txt";
    const char* compressedFile = "compressed.bin";
    const char* decompressedFile = "decompressed.txt";

    // 压缩文件
    compressFile(inputFile, compressedFile);
    printf("File compressed successfully.\n");

    // 解压文件
    decompressFile(compressedFile, decompressedFile);
    printf("File decompressed successfully.\n");

    return 0;
}

上述代码只是用于说明基本思路,并未实现具体的压缩算法。需要在compressFiledecompressFile函数中实现实际的压缩和解压算法逻辑。

compressFile函数中,打开输入文件(例如input.txt),读取文件内容并进行压缩处理,最后将压缩后的数据写入到输出文件(例如compressed.bin)中。

decompressFile函数中,打开压缩文件(例如compressed.bin),读取压缩数据并进行解压处理,最后将解压后的数据写入到输出文件(例如decompressed.txt)中。

可以选择使用现成的压缩算法库,如zlib、gzip等,或者自行实现一种简单的压缩算法(例如LZ77)。

下面章节介绍使用LZ77算法实现压缩解压。

3.2 完整的实现

LZ77(Lempel-Ziv-Welch 1977)是一种基于字典的无损数据压缩算法,常用于文件压缩和网络传输中。通过利用数据中的重复片段来实现压缩,并且可以实现逐步的解压缩。

LZ77算法的核心思想是使用一个滑动窗口和一个向前看缓冲区来寻找重复出现的字符串。算法从输入数据的开头开始,逐步读取数据并尝试匹配滑动窗口中已经出现过的字符串,如果找到匹配的字符串,就将其表示为(偏移,长度)的形式,并且在输出中只保留没有匹配的字符,然后向前滑动窗口和向前看缓冲区,继续下一轮匹配。如果没有找到匹配的字符串,则将当前字符作为新的字符串添加到滑动窗口,并输出它。

下面是LZ77算法的详细步骤:

(1)初始化滑动窗口和向前看缓冲区。

(2)从输入数据中读取一个字符作为当前字符。

(3)在滑动窗口中查找最长的匹配字符串,该字符串与向前看缓冲区中的部分或全部字符匹配。如果有多个匹配字符串具有相同的长度,选择最靠近滑动窗口末尾的字符串。

(4)如果找到匹配字符串:

  • 记录该匹配字符串的偏移(滑动窗口中的位置)和长度。
  • 将未匹配的字符添加到输出,并将滑动窗口和向前看缓冲区更新为匹配之后的位置。

(5)如果未找到匹配字符串:

  • 将当前字符作为新的字符串添加到滑动窗口。
  • 将当前字符添加到输出。
  • 将滑动窗口和向前看缓冲区更新为下一个位置。

(6)重复步骤2至步骤5,直到遍历完整个输入数据。

(7)输出压缩结果。

LZ77算法的优点是简单易懂,实现相对容易,并且可以提供不错的压缩率。它也有一些限制,例如在处理长重复字符串时效率较低,并且可能会导致压缩结果略微变大。为了克服这些限制,通常会结合其他压缩算法(如Huffman编码)来进一步压缩LZ77的输出结果,以获得更好的压缩效果。

下面使用C语言自行实现的LZ77压缩和解压算法完成压缩和解压:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_WINDOW_SIZE 4096   // 窗口大小
#define MAX_LOOKAHEAD_SIZE 16  // 向前看缓冲区大小

typedef struct {
    int offset;  // 指向匹配字符串在滑动窗口中的偏移量
    int length;  // 匹配字符串的长度
    char nextChar;  // 下一个字符
} Match;

void compressFile(const char* inputFile, const char* outputFile) {
    FILE* input = fopen(inputFile, "rb");
    FILE* output = fopen(outputFile, "wb");
    if (input == NULL || output == NULL) {
        printf("Failed to open files\n");
        return;
    }

    unsigned char window[MAX_WINDOW_SIZE];
    unsigned char lookahead[MAX_LOOKAHEAD_SIZE];

    int windowPos = 0;
    int lookaheadPos = 0;

    // 初始化窗口和向前看缓冲区
    memset(window, 0, sizeof(window));
    fread(lookahead, 1, MAX_LOOKAHEAD_SIZE, input);
    int bytesRead = ftell(input);

    while (bytesRead > 0) {
        Match longestMatch = {0, 0, lookahead[0]};

        // 在窗口中查找最长匹配
        for (int i = windowPos - 1; i >= 0 && i >= windowPos - MAX_WINDOW_SIZE; --i) {
            int len = 0;
            while (len < MAX_LOOKAHEAD_SIZE && lookahead[len] == window[(i + len) % MAX_WINDOW_SIZE]) {
                ++len;
            }
            if (len > longestMatch.length) {
                longestMatch.offset = windowPos - i - 1;
                longestMatch.length = len;
                longestMatch.nextChar = lookahead[len];
            }
        }

        // 写入最长匹配的偏移和长度
        fwrite(&longestMatch, sizeof(Match), 1, output);

        // 更新窗口和向前看缓冲区
        for (int i = 0; i < longestMatch.length + 1; ++i) {
            window[windowPos] = lookahead[i];
            windowPos = (windowPos + 1) % MAX_WINDOW_SIZE;
            if (bytesRead > 0) {
                if (fread(lookahead, 1, 1, input) == 1) {
                    bytesRead = ftell(input);
                } else {
                    bytesRead = 0;
                }
            }
        }
    }

    fclose(input);
    fclose(output);
}

void decompressFile(const char* compressedFile, const char* outputFile) {
    FILE* input = fopen(compressedFile, "rb");
    FILE* output = fopen(outputFile, "wb");
    if (input == NULL || output == NULL) {
        printf("Failed to open files\n");
        return;
    }

    unsigned char window[MAX_WINDOW_SIZE];
    unsigned char lookahead[MAX_LOOKAHEAD_SIZE];

    int windowPos = 0;
    int lookaheadPos = 0;

    // 初始化窗口和向前看缓冲区
    memset(window, 0, sizeof(window));
    fread(lookahead, 1, MAX_LOOKAHEAD_SIZE, input);
    int bytesRead = ftell(input);

    while (!feof(input)) {
        Match match;

        // 从压缩文件读取匹配信息
        fread(&match, sizeof(Match), 1, input);

        // 从窗口中复制匹配字符串到输出文件
        for (int i = 0; i < match.length; ++i) {
            unsigned char ch = window[(windowPos - match.offset + i) % MAX_WINDOW_SIZE];
            fwrite(&ch, 1, 1, output);
        }

        // 写入下一个字符
        fwrite(&match.nextChar, 1, 1, output);

        // 更新窗口和向前看缓冲区
        for (int i = 0; i < match.length + 1; ++i) {
            window[windowPos] = match.nextChar;
            windowPos = (windowPos + 1) % MAX_WINDOW_SIZE;
            if (bytesRead > 0) {
                if (fread(lookahead, 1, 1, input) == 1) {
                    bytesRead = ftell(input);
                } else {
                    bytesRead = 0;
                }
            }
        }
    }

    fclose(input);
    fclose(output);
}

int main() {
    const char* inputFile = "input.txt";
    const char* compressedFile = "compressed.bin";
    const char* decompressedFile = "decompressed.txt";

    // 压缩文件
    compressFile(inputFile, compressedFile);
    printf("File compressed successfully.\n");

    // 解压文件
    decompressFile(compressedFile, decompressedFile);
    printf("File decompressed successfully.\n");

    return 0;
}

上面代码里实现了LZ77压缩和解压算法。在压缩过程中,通过读取输入文件并根据滑动窗口中的匹配信息,将最长匹配的偏移和长度写入到输出文件。在解压过程中,从压缩文件中读取匹配信息,并根据偏移和长度将匹配的字符串复制到输出文件中。

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

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

相关文章

Python支持下Noah-MP陆面模式站点、区域模拟及可视化分析

熟悉陆表过程的主要研究内容以及陆面模型在生态水文研究中的地位和作用&#xff1b;深入理解Noah-MP 5.0模型的原理&#xff0c;掌握Noah-MP模型&#xff08;2023年最新发布的5.0版本&#xff09;所需的系统环境与编译环境的搭建方法及模型实践运行&#xff0c;熟悉linux系统操…

高忆管理大盘行情:美股开盘时间?

股票商场是一个充满生机的环境&#xff0c;而美股作为全球最具生机和最具影响力的股票商场之一&#xff0c;被投资者和买卖员广泛关注。美股开盘时刻是众多投资者和买卖员最为关心的问题之一&#xff0c;由于不同的买卖时刻会对在商场中进行投资产生重要影响。那么&#xff0c;…

虹科分享 | 如何构建深度防御层 | 自动移动目标防御

在当今的威胁环境中&#xff0c;多层纵深防御是安全团队获得安心的唯一方法之一。 为什么?有两个原因: 1、攻击面越来越大 随着DevOps等远程工作和数字化转型项目的兴起&#xff0c;攻击面已经超出了大多数安全团队的定义能力。创建一个完全安全的网络边界是不可能的。正如…

计算机视觉-图形处理基础

一、环境安装 1、anaconda安装 官网下载&#xff1a;Free Download | Anaconda 2、打开anaconda prompt测试是否安装成功 输入conda env list命令显示虚拟环境清单即可 二、创建开发虚拟环境 1、创建python3.8虚拟环境 conda create --name demo_py3.8 python3.8 2、激…

ubuntu18 编译ROS代码 报“cxx_attribute_deprecated”is not know to CXX compiler

编译代码报错如下&#xff1a; 通常用两种解决方式&#xff1a; 1&#xff1a;先检查一下 编写的CMakeList.txt的文件中&#xff0c;所包含、链接的文件和库是否都存在且引用正确&#xff08;仔细的查看了一下&#xff0c;都是正确的&#xff09; 2&#xff1a;查看一下cmake…

iOS 17 及 Xcode 15.0 Beta7 问题记录

1、iOS 17 真机调试问题 iOS 17之后&#xff0c;真机调试Beta版本必须使用Beta版本的Xcode来调试&#xff0c;用以前复制DeviceSupport 方式无法调试&#xff0c;新的Beta版本Xcode中&#xff0c;已经不包含 iOS 17目录。如下图&#xff1a; 解决方案&#xff1a; 1&#x…

数据驱动的胜利:体育赛事的可视化之道

当前&#xff0c;体育赛事遍地开花&#xff0c;智能体育也不断成熟。体育赛事不断与物联网、数字孪生等新一代信息技术深入融合&#xff0c;创新运动方式&#xff0c;推动全民健身&#xff0c;促进产业升级。数字孪生可视化平台易知微也一直致力于体育领域的数字化发展&#xf…

Elexcon2023深圳国际电子展开幕,飞凌嵌入式重装亮相!

8月23日&#xff0c;Elexcon2023深圳国际电子展正式开幕&#xff0c;近600家全球优质品牌厂商齐聚深圳会展中心&#xff08;福田&#xff09;&#xff0c;并有上万名观众来到现场参观、交流。飞凌嵌入式携多款多类型的智能主控产品及热门行业解决方案亮相盛会&#xff0c;展位号…

基于 SpringBoot+Vue+Java 的财务管理系统,附源码,教程

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 一 简介第二.主要技术第三、部分效果图第四章 系统设计4.1功能结构4.2 数据库设计4.2.1 数据库E/R…

如何使用图形数据库构建实时推荐引擎

推荐&#xff1a;使用 NSDT场景编辑器助你快速搭建可二次编辑的3D应用场景 “这是给你的”&#xff0c;“为你推荐的”或“你可能也喜欢”&#xff0c;是大多数数字业务中必不可少的短语&#xff0c;特别是在电子商务或流媒体平台中。 尽管它们看起来像一个简单的概念&#xf…

Linux 设置mysql开机自启动和安装JDK

0目录 1.mysql设置开机自启动 2.linux安装jdk 1.mysql设置开机自启动 去到cd /etc/rc.d/init.d目录 创建一个sh脚本 编辑脚本 设置开机自启动 重启 检查是否自启动 2.linux安装jdk 下载安装包 放在opt目录下 新建soft文件夹&#xff08;opt目录下&#xff09;…

window版本的kibana的安装,使用,启动

前言&#xff1a;本篇是根据小破站UP主遇见狂神说学习篇&#xff0c; 这里是我做的笔记&#xff0c;&#xff08;ElasticSearch7.6入门学习笔记-KuangStudy-文章&#xff09;&#xff0c;up主的文档肯定比我更清晰&#xff0c;但我还是记录一下嘿嘿嘿&#xff0c;下面直接开始 …

用户管理与用户权限

数据库用户管理 新建用户 CREATE USER 用户名来源地址 [IDENTIFIED BY [PASSWORD] 密码]; 用户名&#xff1a;指定将创建的用户名. 来源地址&#xff1a;指定新创建的用户可在哪些主机上登录&#xff0c;可使用IP地址、网段、主机名的形式&#xff0c;本地用户可用localhost&…

【全站最全】被苹果、谷歌和Microsoft停产的产品(三)

2016 Narrative Clip 2012 – 2016 HARDWARE Narrative Clip was small wearable capable of automatically take a picture every 30 seconds whilst being worn throughout the day, a practice known as "life-logging". Pebble 2013 – 2016 HARDWARE Pe…

C#+WPF上位机开发(模块化+反应式)作者:重庆教主

在上位机开发领域中&#xff0c;C#与C两种语言是应用最多的两种开发语言&#xff0c;在C语言中&#xff0c;与之搭配的前端框架通常以QT最为常用&#xff0c;而C#语言中&#xff0c;与之搭配的前端框架是Winform和WPF两种框架。今天我们主要讨论一下C#和WPF这一对组合在上位机开…

SUI 将通过 SUI Foundation 资助 Footprint Analytics 解析其公链数据,为生态系统提供支持

2023 年 8 月 23 日消息&#xff0c;SUI 基金会宣布将赞助链上数据提供商 Footprint Analytics&#xff0c;旨在协助解析公链数据&#xff0c;为 SUI 的生态系统及其开发者提供全方位支持。 Footprint Analytics 将提供完整的数据报表和 API 接口&#xff0c; SUI 链的用户可以…

uniapp离线打包apk - Android Studio

uniapp 离线打包 基于uni-app的andiord 离线打包 开发工具及所需要的jar包​1.将下载的App离线SDK解压打开&#xff0c;找到HBuilder-Integrate-AS &#xff0c;在Android Studio打开2.打开HBuilder X&#xff0c;发行->原生app本地打包->生成本地打包app资源3.在“HBuil…

GitLab-CI 指南

GitLab CI 指南 前置工作 部署GitLab 部署GitLab-Runner 注册Runner到GitLab docker exec -it gitlab-runner bash # 进入容器 gitlab-runner register #调用register命令开始注册 # 在Gitlab Setting中找到Runners,如下图所示Enter the GitLab instance URL (for example, …

做不做软测都能学的技能,一招化解磁盘空间不足!

如&#xff0c;我有一台服务器&#xff0c;磁盘空间为 50g 现在&#xff0c;使用了一段时间之后&#xff0c;磁盘空间不够了 磁盘空间不够&#xff0c;这个时候&#xff0c;如果你再执行某些写入磁盘的操作就会报错&#xff0c;无法执行。 测试服务器磁盘空间不够&#xff0c;…

最适合运动的耳机类型是什么、适合运动的耳机推荐

作为一位热衷于运动的爱好者&#xff0c;对于运动装备的要求十分严格。家里有很多手环和跑鞋&#xff0c;但在跑步时最喜欢的是听歌。一首好曲子能够改善跑步体验&#xff0c;延缓疲劳感。当然&#xff0c;并非所有的耳机都适合运动使用&#xff0c;选择运动耳机时需要考虑到运…