【信息论与编码】【北京航空航天大学】实验二、哈夫曼编码【C语言实现】(下)图像编码压缩

本文主要是介绍【信息论与编码】【北京航空航天大学】实验二、哈夫曼编码【C语言实现】(下)图像编码压缩,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

实验2 哈夫曼编码(下)(图像编码)

实验简介: 本次实验为实验1:哈夫曼编码的后续补充,实验1见博客:实验一、哈夫曼编码【C语言实现】(上)

说明: 实验代码仅作为学习使用,欢迎转载、改进,禁止用于任何商业用途。

一、实验内容

“不同格式的压缩”:

1、问题: 使用画图软件或者其他工具进行一些简单的艺术创作(推荐使用三四种颜色,不要太多,尽量使用较大的分辨率(例如3840 x 2160)),分别将图片保存为 bmpjpeg 格式,尝试使用你在必选模组实现的程序编码图片,对比地解释为什么文件体积会发生这种变化。类似地,尝试编码一个可执行文件(.exe格式),并尝试解释文件体积的变化

2、C语言代码实现

整个项目分为以下5个源代码文件:

(1)pq.h
文件说明:优先队列头文件。
文件内容:

//pq.h:优先队列
#ifndef _PRIORITY_QUEUE_H
#define _PRIORITY_QUEUE_H// =============KeyValue Struct 结构体:键值对=============
typedef struct key_value_struct KeyValue;
struct key_value_struct
{int _key;void *_value;
};
KeyValue *key_value_new(int key, void *value);
void key_value_free(KeyValue *kv, void (*freevalue)(void *));// =============PriorityQueue Struct 结构体:优先队列=============
#define PRIORITY_MAX 1
#define PRIORITY_MIN 2
typedef struct priority_queue_struct PriorityQueue;
struct priority_queue_struct
{KeyValue **_nodes;int _size;int _capacity;int _priority;
};// Some Declarations 相关声明 
PriorityQueue *priority_queue_new(int priority);
void priority_queue_free(PriorityQueue *pq, void (*freevalue)(void *));
const KeyValue *priority_queue_top(PriorityQueue *pq);
KeyValue *priority_queue_dequeue(PriorityQueue *pq);
void priority_queue_enqueue(PriorityQueue *pq, KeyValue *kv);
int priority_queue_size(PriorityQueue *pq);
int priority_queue_empty(PriorityQueue *pq);
void priority_queue_print(PriorityQueue *pq);
#endif

(2)pq.c

文件说明:优先队列函数实现。
文件内容:

//pq.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pq.h"//Private Functions Declarations
static void priority_queue_realloc(PriorityQueue *pq);
static void priority_queue_adjust_head(PriorityQueue *pq);
static void priority_queue_adjust_tail(PriorityQueue *pq);
static int priority_queue_compare(PriorityQueue *pq, int pos1, int pos2);
static void priority_queue_swap(KeyValue **nodes, int pos1, int pos2);//Functions of KeyValue Struct
// 键、值对结构之构造函数
KeyValue *key_value_new(int key, void *value)
{KeyValue *pkv = (KeyValue *)malloc(sizeof(KeyValue));pkv->_key = key;pkv->_value = value;return pkv;
}// 释放键、值对结构之内存
void key_value_free(KeyValue *kv, void (*freevalue)(void *))
{if(kv){if(freevalue){freevalue(kv->_value);}free(kv);}
}//Functions of PriorityQueue Struct
// 优先队列之构造函数
PriorityQueue *priority_queue_new(int priority)
{PriorityQueue *pq = (PriorityQueue *)malloc(sizeof(PriorityQueue));pq->_capacity = 11; //default initial valuepq->_size = 0;pq->_priority = priority;pq->_nodes = (KeyValue **)malloc(sizeof(KeyValue *) * pq->_capacity);return pq;
}// 释放优先队列之内存
void priority_queue_free(PriorityQueue *pq, void (*freevalue)(void *))
{int i;if(pq){for(i = 0; i < pq->_size; ++i)key_value_free(pq->_nodes[i], freevalue);free(pq->_nodes);free(pq);}
}// 取优先队列的队顶元素
const KeyValue *priority_queue_top(PriorityQueue *pq)
{if(pq->_size > 0)return pq->_nodes[0];return NULL;
}// 出个队
KeyValue *priority_queue_dequeue(PriorityQueue *pq)
{KeyValue *pkv = NULL;if(pq->_size > 0){pkv = pq->_nodes[0];priority_queue_adjust_head(pq);}return pkv;
}// 入个队
void priority_queue_enqueue(PriorityQueue *pq, KeyValue *kv)
{printf("add key:%d\n", kv->_key);pq->_nodes[pq->_size] = kv;priority_queue_adjust_tail(pq);if(pq->_size >= pq->_capacity)priority_queue_realloc(pq);
}// 多大的队?
int priority_queue_size(PriorityQueue *pq)
{return pq->_size;
}// 空不空?
int priority_queue_empty(PriorityQueue *pq)
{return pq->_size <= 0;
}// 输出一下
void priority_queue_print(PriorityQueue *pq)
{int i;KeyValue *kv;printf("data in the pq->_nodes\n");for(i = 0; i < pq->_size; ++i)printf("%d ", pq->_nodes[i]->_key);printf("\n");printf("dequeue all data\n");while(!priority_queue_empty(pq)){kv = priority_queue_dequeue(pq);printf("%d ", kv->_key);}printf("\n");
}// 重新分配内存
static void priority_queue_realloc(PriorityQueue *pq)
{pq->_capacity = pq->_capacity * 2;pq->_nodes = realloc(pq->_nodes, sizeof(KeyValue *) * pq->_capacity);
}// 头部略作调整
static void priority_queue_adjust_head(PriorityQueue *pq)
{int i, j, parent, left, right;i = 0, j = 0;parent = left = right = 0;priority_queue_swap(pq->_nodes, 0, pq->_size - 1);pq->_size--;while(i < (pq->_size - 1) / 2){parent = i;left = i * 2 + 1;right = left + 1;j = left;if(priority_queue_compare(pq, left, right) > 0)j++;if(priority_queue_compare(pq, parent, j) > 0){priority_queue_swap(pq->_nodes, i, j);i = j;}elsebreak;}}// 尾部略作调整
static void priority_queue_adjust_tail(PriorityQueue *pq)
{int i, parent, child;i = pq->_size - 1;pq->_size++;while(i > 0){child = i;parent = (child - 1) / 2;if(priority_queue_compare(pq, parent, child) > 0){priority_queue_swap(pq->_nodes, child, parent);i = parent;}elsebreak;}
}// 比个大小
static int priority_queue_compare(PriorityQueue *pq, int pos1, int pos2)
{int adjust = -1;int r = pq->_nodes[pos1]->_key - pq->_nodes[pos2]->_key;if(pq->_priority == PRIORITY_MAX)r *= adjust;return r;
}// 交换2个优先队列
static void priority_queue_swap(KeyValue **nodes, int pos1, int pos2)
{KeyValue *temp = nodes[pos1];nodes[pos1] = nodes[pos2];nodes[pos2] = temp;
}

(3)compress.h

文件说明:压缩函数相关头文件。
文件内容:

//compress.h
#ifndef _FILE_COMPRESSION_H
#define _FILE_COMPRESSION_H//Huffman Tree Node 哈夫曼树结点结构
typedef struct HaffumanTreeNode HTN;
struct HaffumanTreeNode
{char _ch;   //characterint _count; //frequencystruct HaffumanTreeNode *_left; //left childstruct HaffumanTreeNode *_right;//right child
};//FileCompress Struct 文件压缩结构
#define BITS_PER_CHAR 8     //the number of bits in a char
#define MAX_CHARS 256            //the max number of chars
#define FILE_BUF_SIZE 8192  //the size of Buffer for FILE I/Otypedef struct FileCompressStruct FCS;struct FileCompressStruct
{HTN *_haffuman;        //A pointer to the root of hafumman treeunsigned int _charsCount; //To store the number of charsunsigned int _total; //Total bytes in a file.char *_dictionary[MAX_CHARS]; //to store the encoding of each characterint _statistic[MAX_CHARS]; //To store the number of each character
};// Function Prototypes 函数原型
FCS *fcs_new();
void fcs_compress(FCS *fcs, const char *inFileName, const char *outFileName);
void fcs_decompress(FCS *fcs, const char *inFileName, const char *outFileName);
void fcs_free(FCS *fcs);#endif

(4)compress.c

文件说明:压缩相关的函数实现。
文件内容:

//compress.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "compress.h"
#include "pq.h"// 掩码
static const unsigned char mask[8] = 
{ 0x80, /* 10000000 */0x40, /* 01000000 */0x20, /* 00100000 */0x10, /* 00010000 */0x08, /* 00001000 */0x04, /* 00000100 */0x02, /* 00000010 */0x01  /* 00000001 */                        
};//static functions of HTN 哈夫曼树结点相关函数
// 构造函数
static HTN *htn_new(char ch, int count)
{HTN *htn = (HTN *)malloc(sizeof(HTN));htn->_left = NULL;htn->_right = NULL;htn->_ch = ch;htn->_count = count;return htn;
}// 递归打印函数
static void htn_print_recursive(HTN *htn, int depth)
{int i;if(htn){for(i = 0; i < depth; ++i)printf("  ");printf("%d:%d\n", htn->_ch, htn->_count);htn_print_recursive(htn->_left, depth + 1);htn_print_recursive(htn->_right, depth + 1);}
}// 遍历整棵树
static void htn_print(HTN *htn)
{ htn_print_recursive(htn, 0);
}// 释放树的内存
static void htn_free(HTN *htn)
{if(htn){htn_free(htn->_left);htn_free(htn->_right);free(htn);}
}//static functions of FCS 文件压缩相关函数
static void fcs_generate_statistic(FCS *fcs, const char *inFileName)
{int ret, i;unsigned char buf[FILE_BUF_SIZE];FILE *pf = fopen(inFileName, "rb");if(!pf){fprintf(stderr, "can't open file:%s\n", inFileName);return;}while((ret = fread(buf, 1, FILE_BUF_SIZE, pf)) > 0){fcs->_total += ret;for(i = 0; i < ret; ++i){if(fcs->_statistic[buf[i]] == 0)fcs->_charsCount++;fcs->_statistic[buf[i]]++;}}fclose(pf);
}static void fcs_create_haffuman_tree(FCS *fcs)
{int i, count;HTN *htn, *parent, *left, *right;KeyValue *kv, *kv1, *kv2;PriorityQueue *pq;pq = priority_queue_new(PRIORITY_MIN);for(i = 0; i < MAX_CHARS; ++i){if(fcs->_statistic[i]){htn = htn_new((char)i, fcs->_statistic[i]);kv = key_value_new(fcs->_statistic[i], htn);priority_queue_enqueue(pq, kv);}}//fprintf(stdout, "the number of haffuman leaf is %d\n", priority_queue_size(pq));while(!priority_queue_empty(pq)){//fprintf(stdout, "priority queue size:%d\n", priority_queue_size(pq));kv1 = priority_queue_dequeue(pq);kv2 = priority_queue_dequeue(pq);if(kv2 == NULL){fcs->_haffuman = kv1->_value;key_value_free(kv1, NULL);}else{left = (HTN *)kv1->_value;right = (HTN *)kv2->_value;count = left->_count + right->_count;key_value_free(kv1, NULL);key_value_free(kv2, NULL);parent = htn_new(0, count);parent->_left = left;parent->_right = right;kv = key_value_new(count, parent);priority_queue_enqueue(pq, kv);}}priority_queue_free(pq, NULL);//htn_print(fcs->_haffuman);
}static void fcs_generate_dictionary_recursively(HTN *htn, char *dictionary[], char path[], int depth)
{char *code = NULL;if(htn){if(htn->_left == NULL && htn->_right == NULL){code = (char *)malloc(sizeof(char) * (depth + 1));memset(code, 0, sizeof(char) * (depth + 1));memcpy(code, path, depth);dictionary[(unsigned char)htn->_ch] = code;}if(htn->_left){path[depth] = '0';fcs_generate_dictionary_recursively(htn->_left, dictionary, path, depth + 1);}if(htn->_right){path[depth] = '1';fcs_generate_dictionary_recursively(htn->_right, dictionary, path, depth + 1);}}
}static void fcs_generate_dictionary(FCS *fcs)
{char path[32];fcs_generate_dictionary_recursively(fcs->_haffuman, fcs->_dictionary, path, 0);//fcs_print_dictionary(fcs);
}static void fcs_print_dictionary(FCS *fcs)
{int i;for(i = 0; i < MAX_CHARS; ++i)if(fcs->_dictionary[i] != NULL)fprintf(stdout, "%d:%s\n", i, fcs->_dictionary[i]);
}static void fcs_write_statistic(FCS *fcs, FILE *pf)
{int i;fprintf(pf, "%d\n", fcs->_charsCount);for(i = 0; i < MAX_CHARS; ++i)if(fcs->_statistic[i] != 0)fprintf(pf, "%d %d\n", i, fcs->_statistic[i]); 
}// 文件压缩
static void fcs_do_compress(FCS *fcs, const char *inFileName, const char* outFileName)
{int i, j, ret;char *dictEntry, len;unsigned int bytes;char bitBuf;int bitPos;unsigned char inBuf[FILE_BUF_SIZE];FILE *pfIn, *pfOut;pfIn = fopen(inFileName, "rb");if(!pfIn){fprintf(stderr, "can't open file:%s\n", inFileName);return;}pfOut = fopen(outFileName, "wb");if(!pfOut){fclose(pfIn);fprintf(stderr, "can't open file:%s\n", outFileName);return;}fcs_write_statistic(fcs, pfOut);bitBuf = 0x00;bitPos = 0;bytes = 0;while((ret = fread(inBuf, 1, FILE_BUF_SIZE, pfIn)) > 0){for(i = 0; i < ret; ++i){len = strlen(fcs->_dictionary[inBuf[i]]);dictEntry = fcs->_dictionary[inBuf[i]];//printf("%s\n", dictEntry);for(j = 0; j < len; ++j){if(dictEntry[j] == '1'){bitBuf |= mask[bitPos++];}else{bitPos++;}if(bitPos == BITS_PER_CHAR){fwrite(&bitBuf, 1, sizeof(bitBuf), pfOut);bitBuf = 0x00;bitPos = 0;bytes++;}}}}if(bitPos != 0){fwrite(&bitBuf, 1, sizeof(bitBuf), pfOut);bytes++;}fclose(pfIn);fclose(pfOut);printf("The compression ratio is:%f%%\n",(fcs->_total - bytes) * 100.0 / fcs->_total);
}static void fcs_read_statistic(FCS *fcs, FILE *pf)
{int i, charsCount = 0;int ch;int num;fscanf(pf, "%d\n", &charsCount);fcs->_charsCount = charsCount;for(i = 0; i < charsCount; ++i){fscanf(pf, "%d %d\n", &ch, &num);fcs->_statistic[(unsigned int)ch] = num;fcs->_total += num;}
}// 文件解压缩
static void fcs_do_decompress(FCS *fcs, FILE *pfIn, const char *outFileName)
{int i, j, ret;unsigned char ch;HTN *htn;unsigned char buf[FILE_BUF_SIZE];unsigned char bitCode;int bitPos;FILE *pfOut;pfOut = fopen(outFileName, "wb");if(!pfOut){fprintf(stderr, "can't open file:%s\n", outFileName);return;}htn = fcs->_haffuman;bitCode = 0x00;bitPos = 0;while((ret = fread(buf, 1, FILE_BUF_SIZE, pfIn)) > 0){for(i = 0; i < ret; ++i){ch = buf[i];for(j = 0; j < BITS_PER_CHAR; ++j){if(ch & mask[j]){htn = htn->_right;      }else{htn = htn->_left;}if(htn->_left == NULL && htn->_right == NULL) //leaf{if(fcs->_total > 0){fwrite(&htn->_ch, 1, sizeof(char), pfOut);fcs->_total--;}htn = fcs->_haffuman;}}}}fclose(pfOut);
}//FCS functions
FCS *fcs_new()
{FCS *fcs = (FCS *)malloc(sizeof(FCS));fcs->_charsCount = 0;fcs->_total = 0;memset(fcs->_statistic, 0, sizeof(fcs->_statistic));memset(fcs->_dictionary, 0, sizeof(fcs->_dictionary));fcs->_haffuman = NULL;return fcs;
}void fcs_free(FCS *fcs)
{int i;if(fcs){if(fcs->_haffuman)htn_free(fcs->_haffuman);for(i = 0; i < MAX_CHARS; ++i)free(fcs->_dictionary[i]);free(fcs);}
}void fcs_compress(FCS *fcs, const char *inFileName, const char *outFileName)
{fprintf(stdout, "To compress file: %s ...\n", inFileName); fcs_generate_statistic(fcs, inFileName);fcs_create_haffuman_tree(fcs);fcs_generate_dictionary(fcs);fcs_do_compress(fcs, inFileName, outFileName);fprintf(stdout, "The compressed data of file: %s stored at %s!\n",inFileName, outFileName);
}void fcs_decompress(FCS *fcs, const char *inFileName, const char *outFileName)
{FILE *pfIn;fprintf(stdout, "To decompress file: %s ...\n", inFileName);pfIn= fopen(inFileName, "rb");if(!pfIn){fprintf(stderr, "can't open file: %s\n", inFileName);return ;}fcs_read_statistic(fcs, pfIn);fcs_create_haffuman_tree(fcs);fcs_generate_dictionary(fcs);fcs_do_decompress(fcs, pfIn, outFileName);fclose(pfIn);fprintf(stdout, "The decompressed data of file: %s stored at %s\n",inFileName, outFileName);
}

(5)main.c

文件说明: 自定义了压缩后的文件后缀名:.hfm(哈夫曼Huffman的简写)
文件内容:

//main.c
#include <stdlib.h>
#include "compress.h"// 常量定义
const int DO_COMPRESS = 1;
const int DO_DECOMPRESS = 1;
const char *InFile = "tree.jpg"; //The file to compress 要压缩的文件
const char *CompressedFile = "tree.hfm"; //Compressed data of the file 压缩后的文件
const char *OutFile = "tree_decompressed.jpg"; //The decompressed file of the data 解压缩后的文件// 主函数
int main(int argc, char **argv)
{//1. compress file 对文件进行压缩if(DO_COMPRESS){FCS *fcs1;fcs1 = fcs_new();fcs_compress(fcs1, InFile, CompressedFile);fcs_free(fcs1);}//2. decompress file 对压缩后的文件进行解压缩if(DO_DECOMPRESS){FCS *fcs2;fcs2 = fcs_new();fcs_decompress(fcs2, CompressedFile, OutFile);fcs_free(fcs2);}system("pause");return 0;
}

3、图片压缩测试

(1)、测试文件1:tree.jpg
文件说明:手绘的简笔画,内容为一棵树,格式为 .jpg

main.c中输入的相关字段:

const char *InFile = "tree.jpg"; //The file to compress 要压缩的文件
const char *CompressedFile = "tree.hfm"; //Compressed data of the file 压缩后的文件
const char *OutFile = "tree_decompressed.jpg"; //The decompressed file of the data 解压缩后的文件

运行时截图:

在这里插入图片描述

压缩的文件大小:276,764字节
压缩的文件大小:242,500字节
压缩导致改变的百分比:约12.4%

(2)、测试文件2:plane.bmp

文件说明:手绘的简笔画,内容为一架飞机,格式为 .bmp

main.c中输入的相关字段:

const char *InFile = "plane.bmp"; //The file to compress 要压缩的文件
const char *CompressedFile = "plane.hfm"; //Compressed data of the file 压缩后的文件
const char *OutFile = "plane_decompressed.bmp"; //The decompressed file of the data 解压缩后的文件

运行时截图:

在这里插入图片描述

压缩的文件大小:24,883,254字节
压缩的文件大小:3,775,181字节
压缩导致改变的百分比:约84.8%

4、可执行文件压缩测试

(1)、测试文件1:test.exe
文件说明:某一C语言源程序编译生成的可执行文件,格式为 .exe

main.c中输入的相关字段:

const char *InFile = "test.exe"; //The file to compress 要压缩的文件
const char *CompressedFile = "test.hfm"; //Compressed data of the file 压缩后的文件
const char *OutFile = "test_decompressed.exe"; //The decompressed file of the data 解压缩后的文件

运行时截图:

在这里插入图片描述

压缩的文件大小:72,383字节
压缩的文件大小:49,895字节
压缩导致改变的百分比:约31.1%

(2)、测试文件2:bfv.exe
文件说明:主机游戏《战地风云5》的可执行文件,格式为 .exe

main.c中输入的相关字段:

const char *InFile = "bfv.exe"; //The file to compress 要压缩的文件
const char *CompressedFile = "bfv.hfm"; //Compressed data of the file 压缩后的文件
const char *OutFile = "bfv_decompressed.exe"; //The decompressed file of the data 解压缩后的文件

运行时截图:

在这里插入图片描述

压缩的文件大小:238,028,800字节
压缩的文件大小:45,326,336字节
压缩导致改变的百分比:约81.0%

问题: 在待压缩文件体积过大时,解压缩时可能会出现问题。具体原因仍在研究当中。
至此,本次实验结束。

这篇关于【信息论与编码】【北京航空航天大学】实验二、哈夫曼编码【C语言实现】(下)图像编码压缩的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati

Java Multimap实现类与操作的具体示例

《JavaMultimap实现类与操作的具体示例》Multimap出现在Google的Guava库中,它为Java提供了更加灵活的集合操作,:本文主要介绍JavaMultimap实现类与操作的... 目录一、Multimap 概述Multimap 主要特点:二、Multimap 实现类1. ListMult

C#实现将Office文档(Word/Excel/PDF/PPT)转为Markdown格式

《C#实现将Office文档(Word/Excel/PDF/PPT)转为Markdown格式》Markdown凭借简洁的语法、优良的可读性,以及对版本控制系统的高度兼容性,逐渐成为最受欢迎的文档格式... 目录为什么要将文档转换为 Markdown 格式使用工具将 Word 文档转换为 Markdown(.

Java反射实现多属性去重与分组功能

《Java反射实现多属性去重与分组功能》在Java开发中,​​List是一种非常常用的数据结构,通常我们会遇到这样的问题:如何处理​​List​​​中的相同字段?无论是去重还是分组,合理的操作可以提高... 目录一、开发环境与基础组件准备1.环境配置:2. 代码结构说明:二、基础反射工具:BeanUtils

使用Python实现base64字符串与图片互转的详细步骤

《使用Python实现base64字符串与图片互转的详细步骤》要将一个Base64编码的字符串转换为图片文件并保存下来,可以使用Python的base64模块来实现,这一过程包括解码Base64字符串... 目录1. 图片编码为 Base64 字符串2. Base64 字符串解码为图片文件3. 示例使用注意

使用Python实现获取屏幕像素颜色值

《使用Python实现获取屏幕像素颜色值》这篇文章主要为大家详细介绍了如何使用Python实现获取屏幕像素颜色值,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、一个小工具,按住F10键,颜色值会跟着显示。完整代码import tkinter as tkimport pyau

在Java中将XLS转换为XLSX的实现方案

《在Java中将XLS转换为XLSX的实现方案》在本文中,我们将探讨传统ExcelXLS格式与现代XLSX格式的结构差异,并为Java开发者提供转换方案,通过了解底层原理、性能优势及实用工具,您将掌握... 目录为什么升级XLS到XLSX值得投入?实际转换过程解析推荐技术方案对比Apache POI实现编程

IDEA如何实现远程断点调试jar包

《IDEA如何实现远程断点调试jar包》:本文主要介绍IDEA如何实现远程断点调试jar包的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录问题步骤总结问题以jar包的形式运行Spring Boot项目时报错,但是在IDEA开发环境javascript下编译