【数据结构与算法】Huffman编码/译码(C/C++)

news2024/9/24 13:21:04

实践要求

1. 问题描述

利用哈夫曼编码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编译码系统。


2. 基本要求

一个完整的系统应具有以下功能:

  1. I 初始化(Initialization)
    从终端读入字符集大小n,及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmtree中。
  2. C:编码(Coding)
    利用已建好的哈夫曼树(如不在内存,则从文件hfmtree中读 入),对文件tobetrans中的正文进行编码,然后将结果存入codefile中。
  3. D:译码(Decoding)
    利用已建好的哈夫曼树将文件codefile中的代码进行译 码,结果存入文件textfile中。
  4. P:印代码文件(Print)
    将文件codefile以紧凑格式显示在终端上,每行50个代 码。同时将此字符形式的编码文件写入文件codeprint中。
  5. T:印哈夫曼树(Tree print)
    将已在内存中的哈夫曼树以直观的方式 树或凹入表行式)显示在终端上,同时将此字符形式的哈夫曼树写入文件treeprint中。

3. 测试数据

可能出现8种字符,其概率分别为0.05,0.29,0.07,0.80,0.14,0.23,0.03,0.11试设计赫夫曼编码。

3.1 input

请输入字符串集大小

8

请输入字符和权值 //用空格分离

a 5
b 29
c 7
d 8
e 14
f 23
g 3
h 11

3.2 output

若要输出"abc"这个字符串的Huffman编码
(正确的结果应为0001111010)


实践报告

1. 题目分析

说明程序设计的任务,强调的是程序要做什么,此外列出各成员分工

程序设计任务:设计一个Huffman编码译码系统,有I:初始化;C:编码;D:译码;P:印代码文件;T:印Huffman树五个功能模块。


2. 数据结构设计

说明程序用到的数据结构的定义,主程序的流程及各模块之间的层次关系

该程序主要用到的数据结构是Huffman树(最优二叉树)

主程序流程图

在这里插入图片描述


3. 程序设计

实现概要设计中的数据类型,对主程序、模块及主要操作写出伪代码,画出函数的调用关系

各模块伪代码

初始化

在这里插入图片描述

编码

在这里插入图片描述

解码

在这里插入图片描述

打印

在这里插入图片描述

打印并存入Huffman树

在这里插入图片描述

各函数层次关系图

Initialization

在这里插入图片描述

EnCode

在这里插入图片描述

DeCode

在这里插入图片描述

Tree

在这里插入图片描述


4. 调试分析

程序复杂度分析

1. 空间复杂度
o ( n ) o(n) o(n)
空间复杂度主要是由节点的内存空间和最小堆的内存空间所占据的空间所决定的,因此总的空间复杂度为O(n)。
2. 时间复杂度
O ( n l o g n ) O(nlogn) O(nlogn)
由于建立哈夫曼树的过程中需要使用最小堆来实现节点的排序和合并。最小堆的插入和删除操作都需要O(logn)的时间复杂度,而需要进行n-1次操作,因此总的时间复杂度为O(nlogn)

所遇到的问题的解决方法

  • 问题1:内存泄露问题,动态分配的空间未释放。
    解决:在函数解放使用free来释放动态分配的空间
  • 问题2:按格式读取文件信息
    解决:使用buffer和characters两个数组,buffer用于接收所有的txt文件中的内容,将buffer中有用的信息赋值给characters。

5. 测试结果

列出测试结果,包括输入和输出

测试结果一

先初始化将字符以及其权值存入txt文件中

在这里插入图片描述
在这里插入图片描述

再对其进行编码

在这里插入图片描述
在这里插入图片描述

对编码结果进行解码

在这里插入图片描述

打印Huffman树并存入txt中

在这里插入图片描述


6. 用户使用说明

给出主界面及主要功能界面

使用说明

首先初始化Huffman树,然后进行编码,编码后可选择译码也可以选择印代码文件或者印Huffman树。

7. 附录

源程序文件清单:
huffman.cpp
treeprint.txt
textfile.txt
hfmtree.txt
codefile.txt
codeprint.txt

8. 全部代码

huffman.cpp

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

using namespace std;

// 定义哈夫曼树的节点结构体
typedef struct node
{
    int weight;         // 权值
    char character;     // 字符
    struct node *left;  // 左子树指针
    struct node *right; // 右子树指针
} Node;

// 定义哈夫曼树的节点类型
typedef Node *HuffmanTree;

// 定义哈夫曼树的节点最小堆
typedef struct heap
{
    int size;          // 堆的大小
    int capacity;      // 堆的容量
    HuffmanTree *data; // 堆数据存储指针
} MinHeap;

// 初始化最小堆
MinHeap *initMinHeap(int capacity)
{
    // 动态分配最小堆
    MinHeap *heap = (MinHeap *)malloc(sizeof(MinHeap));
    heap->capacity = capacity;
    heap->size = 0;
    heap->data = (HuffmanTree *)malloc(sizeof(HuffmanTree) * capacity);
    return heap;
}

// 最小堆中插入元素
void insert(MinHeap *heap, HuffmanTree node)
{
    if (heap->size >= heap->capacity)
    {
        return; // 如果堆已满,直接退出
    }
    heap->data[heap->size] = node;  // 将元素插入堆底
    int current = heap->size++;     // 更新堆大小
    int parent = (current - 1) / 2; // 父节点的下标
    // 自下往上调整堆,直到找到合适的位置插入新元素
    while (parent >= 0 && heap->data[current]->weight < heap->data[parent]->weight)
    {
        // 如果当前元素比父节点的权值小,则交换两个元素的位置
        HuffmanTree temp = heap->data[parent];
        heap->data[parent] = heap->data[current];
        heap->data[current] = temp;
        current = parent;
        parent = (current - 1) / 2;
    }
}

// 从最小堆中取出最小元素
HuffmanTree extractMin(MinHeap *heap)
{
    if (heap->size == 0) // 如果堆为空,直接返回空指针
    {
        return NULL;
    }
    HuffmanTree min = heap->data[0];          // 最小元素即为堆顶元素
    heap->data[0] = heap->data[--heap->size]; // 将堆底元素移到堆顶,并更新堆大小
    int current = 0;                          // 当前节点的下标
    int child = current * 2 + 1;              // 当前节点的左孩子的下标
    // 自上往下调整堆,直到找到合适的位置插入堆底元素
    while (child < heap->size) // 当前节点还有孩子节点
    {
        if (child + 1 < heap->size && heap->data[child + 1]->weight < heap->data[child]->weight)
        {
            child++; // 找到当前节点的左右孩子中较小的一个
        }
        if (heap->data[child]->weight < heap->data[current]->weight)
        {
            // 将当前节点和较小孩子节点交换位置
            HuffmanTree temp = heap->data[child];
            heap->data[child] = heap->data[current];
            heap->data[current] = temp;
            current = child;         // 更新当前节点的下标
            child = current * 2 + 1; // 更新当前节点的左孩子下标
        }
        else
        {
            break; // 如果已经满足最小堆的性质,则退出循环
        }
    }
    return min; // 返回被取出的最小元素
}

// 用最小堆构建哈夫曼树
HuffmanTree buildHuffmanTree(int *weights, char *characters, int n)
{
    // 初始化最小堆,将每个字符及其权重转换成节点,并插入堆中
    MinHeap *heap = initMinHeap(n);
    for (int i = 0; i < n; i++)
    {
        Node *node = (Node *)malloc(sizeof(Node));
        node->weight = weights[i];
        node->character = characters[i];
        node->left = NULL;
        node->right = NULL;
        insert(heap, node); // 将节点插入堆中
    }
    // 不断从最小堆中取出权重最小的两个节点,合并成一个新节点,再插入堆中
    while (heap->size > 1)
    {
        HuffmanTree left = extractMin(heap);  // 取出堆顶节点,即最小权重节点
        HuffmanTree right = extractMin(heap); // 再次取出最小权重节点
        Node *parent = (Node *)malloc(sizeof(Node));
        parent->weight = left->weight + right->weight; // 新节点的权重为左右节点的权重之和
        parent->left = left;                           // 将左节点作为新节点的左孩子
        parent->right = right;                         // 将右节点作为新节点的右孩子
        insert(heap, parent);                          // 将新节点插入堆中
    }
    HuffmanTree root = extractMin(heap); // 最后堆中只剩下根节点,即为哈夫曼树的根节点
    free(heap->data);                    // 释放堆数组占用的空间
    free(heap);                          // 释放最小堆结构体占用的空间
    return root;                         // 返回哈夫曼树的根节点指针
}
// 对单个字符进行编码模块
char *encodeChar(HuffmanTree root, char ch)
{
    static char code[100]; // 申请存储编码的空间
    static int index = 0;  // 记录编码位数

    if (root == NULL)
    {
        return NULL;
    }

    if (root->character == ch)
    {
        code[index] = '\0'; // 编码结尾
        index = 0;          // 编码位数归零
        return code;
    }

    code[index++] = '0';
    char *leftCode = encodeChar(root->left, ch);
    if (leftCode != NULL)
    {
        return leftCode;
    }

    index--; // 回溯
    code[index++] = '1';
    char *rightCode = encodeChar(root->right, ch);
    if (rightCode != NULL)
    {
        return rightCode;
    }

    index--; // 回溯
    return NULL;
}

// 对文本进行编码模块
char *encode(HuffmanTree root, char *text)
{
    char *result = (char *)malloc(strlen(text) * 100 * sizeof(char)); // 申请存储编码结果的空间
    result[0] = '\0';                                                 // 初始化
    for (int i = 0; i < strlen(text); i++)
    {
        char *code = encodeChar(root, text[i]); // 对单个字符编码
        if (code)
        {
            strcat(result, code); // 将编码拼接到结果中
        }
    }
    return result;
}

// 初始化函数
HuffmanTree initialization()
{
    int n; // 字符集大小
    printf("请输入字符集大小:\n");
    scanf("%d", &n);
    int *weights = (int *)malloc(sizeof(int) * n);       // 动态分配n个权重值
    char *characters = (char *)malloc(sizeof(char) * n); // 动态分配n个字符
    printf("请输入字符和权值:\n");
    for (int i = 0; i < n; i++)
    {
        scanf(" %c %d", &characters[i], &weights[i]);
    }
    HuffmanTree root = buildHuffmanTree(weights, characters, n);
    FILE *fp = fopen("hfmtree.txt", "w");
    fprintf(fp, "%d\n", n);
    for (int i = 0; i < n; i++)
    {
        fprintf(fp, "%c%s", characters[i], i == n - 1 ? "\n" : " ");
    }
    for (int i = 0; i < n; i++)
    {
        fprintf(fp, "%d%s", weights[i], i == n - 1 ? "\n" : " ");
    }
    fclose(fp);
    return root;
}

void EnCodeChar(HuffmanTree root)
{
    FILE *fp;
    char *characters;
    char buffer[100];
    int n;
    // 打开文件
    fp = fopen("hfmtree.txt", "r");
    if (fp == NULL)
    {
        printf("文件打开失败\n");
        exit(1);
    }
    // 读取第一行,获取字符集大小
    fscanf(fp, "%d", &n);
    // 分配空间
    characters = (char *)malloc(n * sizeof(char));
    // 读取第二行数据
    fgets(buffer, sizeof(characters) * 2, fp);
    fgets(buffer, sizeof(characters) * 2, fp);
    int i = 0;
    int j = 0;
    while (buffer[i] != NULL)
    {
        if (buffer[i] != ' ')
        {
            characters[j] = buffer[i];
            j++;
        }
        i++;
    }
    fclose(fp);
    for (int i = 0; i < n; i++)
    {
        int index = 0;
        char *res = encodeChar(root, characters[i]);
        printf("%c: %s\n", characters[i], res);
    }
}

void EnCode(HuffmanTree root)
{
    char tobetrans[100];
    char *result;
    printf("请输入一个字符串:\n");
    scanf("%s", tobetrans); // 读取输入的字符串,存储到字符数组中
    result = encode(root, tobetrans);
    printf("%s\n", result);
    FILE *fp = fopen("codefile.txt", "w");
    for (int i = 0; i < strlen(result); i++)
    {
        fprintf(fp, "%c", result[i]);
    }
    fclose(fp);
}

char DeCodeChar(HuffmanTree root, char *code)
{
    HuffmanTree p = root;
    while (*code != '\0')
    {
        if (*code == '0')
        {
            p = p->left;
        }
        else if (*code == '1')
        {
            p = p->right;
        }
        if (p->left == NULL && p->right == NULL)
        {
            return p->character;
        }
        code++;
    }
    return '\0';
}

void DeCode(HuffmanTree root)
{
    // 读取译码文件
    FILE *fp_code = fopen("codefile.txt", "r");
    char *code = (char *)malloc(1000 * sizeof(char)); // 申请存储代码的空间
    fscanf(fp_code, "%s", code);                      // 读取代码
    fclose(fp_code);

    // 译码
    char *text = (char *)malloc(1000 * sizeof(char)); // 申请存储译文的空间
    int i = 0, j = 0;
    while (code[i] != '\0')
    {
        char *tmp = (char *)malloc(100 * sizeof(char)); // 申请临时空间存储单个字符的编码
        int k = 0;
        while (DeCodeChar(root, tmp) == '\0')
        {
            tmp[k++] = code[i++];
        }
        text[j++] = DeCodeChar(root, tmp); // 译码并存储译文
        free(tmp);                         // 释放临时空间
    }
    text[j] = '\0';
    // 存储译文到文件中
    FILE *fp_text = fopen("textfile.txt", "w");
    fprintf(fp_text, "%s", text);
    fclose(fp_text);
    // 释放申请的空间
    free(code);
    free(text);
}

void Print()
{
    FILE *fp;
    char buffer[100];
    fp = fopen("codefile.txt", "r");
    if (fp == NULL)
    {
        printf("文件打开失败\n");
        exit(1);
    }
    // 读取数据
    fgets(buffer, 100, fp);
    printf("%s", buffer);
    fclose(fp);
    fp = fopen("codeprint.txt", "w");
    for (int i = 0; i < strlen(buffer); i++)
    {
        fprintf(fp, "%c", buffer[i]);
    }
    fclose(fp);
}

// 打印哈夫曼树的函数
void PrintHfmTree(FILE *file, HuffmanTree root, int level, char *prefix, int is_last)
{
    // 如果根节点为空,则返回
    if (root == NULL)
    {
        return;
    }
    // 打印当前节点的值
    printf("%s", prefix);
    printf(is_last ? "└── " : "├── ");
    printf("(%c:%d)\n", root->character, root->weight);
    fprintf(file, "%s", prefix);
    fprintf(file, is_last ? "└── " : "├── ");
    fprintf(file, "(%c:%d)\n", root->character, root->weight);

    // 更新前缀
    char new_prefix[128];
    sprintf(new_prefix, "%s%s", prefix, is_last ? "    " : "│   ");

    // 递归打印左右子树
    PrintHfmTree(file, root->left, level + 1, new_prefix, (root->right == NULL));
    PrintHfmTree(file, root->right, level + 1, new_prefix, 1);
}

void Tree(HuffmanTree root)
{
    // 打开文件treeprint
    FILE *file = fopen("treeprint.txt", "w");
    // 调用打印函数打印哈夫曼树并写入文件
    PrintHfmTree(file, root, 0, "", 1);
    // 关闭文件
    fclose(file);
}

HuffmanTree root = nullptr; // 将huffman树根设置为全局变量

void Window()
{
    char choice;
    char exit_choice;
    while (1)
    {
        printf("请选择您的操作:\n");
        printf("I. 初始化\n");
        printf("C. 编码\n");
        printf("D. 译码\n");
        printf("P. 印代码文件\n");
        printf("T. 印哈夫曼树\n");
        printf("E. 退出\n");
        scanf(" %c", &choice); // 加上空格忽略换行符

        switch (choice)
        {
        case 'I':
            printf("您选择了初始化操作。\n");
            root = initialization();
            break;
        case 'C':
            if (root == NULL)
            {
                printf("请先进行初始化操作!\n");
                break;
            }
            printf("您选择了编码操作。\n");
            EnCode(root);
            break;
        case 'D':
            if (root == NULL)
            {
                printf("请先进行初始化操作!\n");
                break;
            }
            printf("您选择了译码操作。\n");
            DeCode(root);
            break;
        case 'P':
            printf("您选择了打印操作。\n");
            Print();
            break;
        case 'T':
            printf("您选择了打印操作。\n");
            Tree(root);
            break;
        case 'E':
            printf("您确定要退出吗?按E键确认退出,按其他键返回上级菜单。\n");
            scanf(" %c", &exit_choice); // 加上空格忽略换行符
            if (exit_choice == 'E' || exit_choice == 'e')
            {
                printf("谢谢使用,再见!\n");
                return;
            }
            break;
        default:
            printf("无效的选择,请重新选择。\n");
            break;
        }
    }
}

int main()
{
    Window();
    return 0;
}

codefile.txt

0001111010

codeprint.txt

0001111010

hfmtree.txt

8
a b c d e f g h
5 29 7 8 14 23 3 11

textfile.txt

abc

treeprint.txt

└── (
:100)
    ├── (
:42)
    │   ├── (
:19)
    │   │   ├── (
:8)
    │   │   │   ├── (g:3)
    │   │   │   └── (a:5)
    │   │   └── (h:11)
    │   └── (f:23)
    └── (
:58)
        ├── (
:29)
        │   ├── (e:14)
        │   └── (
:15)
        │       ├── (c:7)
        │       └── (d:8)
        └── (b:29)


结束语

  因为是算法小菜,所以提供的方法和思路可能不是很好,请多多包涵~如果有疑问欢迎大家留言讨论,你如果觉得这篇文章对你有帮助可以给我一个免费的赞吗?我们之间的交流是我最大的动力!

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

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

相关文章

3D渲染的定义和应用领域

三维渲染&#xff08;3D rendering&#xff09;是一种将三维模型数据转化为二维图像的技术&#xff0c;通常利用计算机图形学的方法来实现。通过运用光线、材质、纹理、阴影等效果&#xff0c;将三维物体展现在二维屏幕上&#xff0c;以模拟真实世界中的三维景象。 一、三维渲…

el-table 默认勾选数据

目录 效果图 步骤&#xff1a; 1. 看elementui 官网上的案例&#xff0c;用到的方法是自带的 toggleRowSelection 2. 思路 原委 选中主表中的一条数据&#xff1b;判断与子表中的数据是否关联&#xff08;如果子表的关联ID主表的ID&#xff0c;则子表的这条数据显示被勾选&a…

CADD蛋白结构分析、虚拟筛选、分子对接(蛋白-蛋白、蛋白-

时间:第一天上午 课程名称:生物分子互作基础 课程内容:1.生物分子相互作用研究方法 1.1蛋白-小分子、蛋白-蛋白相互作用原理 1.2 分子对接研究生物分子相互作用 1.3 蛋白蛋白对接研究分子相互作用 课程名称:蛋白数据库 课程内容:1. PDB 数据库介绍 1.1 PDB蛋白数据库功能 1.2 …

Springboot整合jdbc_template

1.构建Springboot项目 利用springboot整合jdbctemplate,并不需要导入其他的依赖&#xff0c;具体的项目结构图如图 2.写domain层 数据库映射的实体类 package com.jkk.springboot_jdbc_template.domain;/*** author jkk*/import lombok.AllArgsConstructor; import lombok…

04 - C++学习笔记: 循环语句和跳转语句

在C编程中&#xff0c;循环语句和跳转语句是非常重要的控制结构。循环语句允许重复执行一段代码&#xff0c;而跳转语句允许在程序执行过程中改变执行的流程。本篇笔记将介绍C中常用的循环语句和跳转语句&#xff0c;并通过例子进行说明。 &#x1f501;循环类型 C 编程语言提…

查询子节点 postgresql

数据库为postgresql WITH RECURSIVE cte AS (SELECTn. ID,n. com_name,n."parentId" AS pidFROMcompany AS nWHEREn. ID = 2UNION ALLSELECTr. ID,r. com_name,cte. ID AS pidFROMcteJOIN company AS r ON r.

轻松实现邮箱验证码功能!快来体验Spring Boot的神奇力量!

邮件验证是现代互联网服务中常用的安全功能&#xff0c;本文介绍如何利用Spring Boot框架快速搭建一个高效易用的邮箱验证码功能。从配置邮箱>发送服务&#xff0c;到编写验证逻辑&#xff0c;无痛实现邮箱验证码功能轻而易举。快来掌握这个技能&#xff0c;加强您的应用安全…

论文解读 | CVPR 2020:PV-RCNN:用于三维物体检测的点体素特征集提取

原创 | 文 BFT机器人 论文《PV-RCNN: Point-Voxel Feature Set Abstraction for 3D Object Detection》是一篇关于三维物体检测的论文。该论文提出了一种名为PV-RCNN的方法&#xff0c;用于从点云数据中进行三维物体检测&#xff0c;并在各种应用中取得了优秀的性能。 论文的主…

数据库第一章

一。数据库 1.概述 数据库database用来存储数据和管理数据的仓库 分类&#xff1a;关系型MySQL/非关系型Redis 关系型数据库&#xff08;二维表格模型&#xff09;&#xff1a;Oracle,MySQL,SQLServer,Access 非关系型数据库&#xff1a;MongoDB&#xff0c;Redis&#xf…

linux 文件锁flock与fcntl bytes级别精细控制不再是困难

​专栏内容&#xff1a; postgresql内核源码分析 手写数据库toadb 并发编程 个人主页&#xff1a;我的主页 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 文件锁 概述 前面博客介绍了多任务下互斥的方法&#xff0c;如…

Docker容器的数据卷

Docker容器的数据卷 一、数据卷概念 概念&#xff1a;数据卷是宿主机中的一个目录或文件 当容器目录和数据卷目录绑定后&#xff0c;对方的修改会立即同步一个数据卷可以被多个容器同时挂载一个容器也可以挂载多个数据卷 可以解决以下问题 可以解决容器数据的持久化&#xff0…

高效学习法

目标明确&#xff0c;难度适中 全面&#xff1a;宏观概述&#xff0c;微观详尽 明确&#xff1a;目标要明确&#xff0c;否则陷入选择漩涡&#xff0c;导致大脑内耗。李白的“行路难&#xff0c;多歧路” 渐进&#xff1a;既要进步&#xff0c;也要逐步…

47 # 实现可读流

上一节讲了 fs.createReadStream 创建一个可读流&#xff0c;那么怎么查看它的源码是怎么实现的&#xff1f; 我们可以采用打断点的方式&#xff1a;我们可以看到先执行了 lazyLoadStreams 如果没有 ReadStream 就会 require 内部的 internal/fs/streams 模块 通过 internal/f…

免费开源 | 基于SpringBoot+Vue的物流管理系统

1-介绍 基于SpringBootvuemybatis-plus的简单的物流管理系统DEMO,前后端分离&#xff0c;可用于扩展基础&#xff0c;可用于简单课设&#xff0c;可用于基础学习 2-技术架构 SpringBootvuemybatis-plusmysql 8.0 3-使用说明 安装数据库demo/sql/wuliu.sql运行后端demo 1-…

QT调用glog日志流程

glog日志库是Google开源的轻量级的日志库&#xff0c;平时在开发过程中经常要使用到日志&#xff0c;本篇记录Qt项目使用glog日志库的记录。 1.首先下载cmake&#xff0c;Download | CMake 安装设置环境变量&#xff0c;检查安装情况 2.下载glog源码 git clone https://git…

指数分布的概率密度推导

指数分布的概率密度&#xff0c;一直理解的不够深入&#xff0c;一直都不明白为什么是这么奇怪的形式&#xff0c;指数位置的分母为什么有个-theta&#xff0c;也一直不太明白该分布的特点&#xff0c;直到看到如下篇博文&#xff1a; 指数分布概率密度推导1 指数分布概率密度…

PyCharm安装配置PyQt5/QtDesigner/PyUic的超详细教程

目录 1.介绍 2.安装与配置 1.下载安装PyQt5 2.QtDesignerPyUic的安装配置 1.下载安装 2.打开designer.exe所在位置 3.配置PyCharm QtDesigner 4.验证安装是否成功 5.PyCharmPyUic快捷菜单工具配置:便于将Qt的UI文件转换成.py文件 6.配置PyQt5 PyRcc:便于将资源文件转码 1…

拒绝裸奔,使用jasypt为SpringBoot配置文件进行加密。

平日使用Github上传代码时&#xff0c;不可避免的会遇到一个问题就是配置文件中的敏感信息的处理&#xff0c;如MySQL的用户名密码&#xff0c;Redis的密码等。而如果一不注意提交到Github后&#xff0c;无异于出门不锁还留把钥匙&#xff0c;后果不堪设想&#xff0c; 近些年开…

随笔-毕业十周年聚会

文章目录 随笔-毕业十周年聚会1. 引子2. 流水账3. 感悟 随笔-毕业十周年聚会 1. 引子 上周三&#xff0c;许久不联系的班长给我发了个微信&#xff0c;问我周六有没有时间&#xff0c;学校和学院组织了毕业十周年校友返校活动&#xff0c;凑着这个机会大家聚一聚。 一时间有…

SpringBoot项目从0到1配置logback日志打印

大家好&#xff01;我是sum墨&#xff0c;一个一线的底层码农&#xff0c;平时喜欢研究和思考一些技术相关的问题并整理成文&#xff0c;限于本人水平&#xff0c;如果文章和代码有表述不当之处&#xff0c;还请不吝赐教。 以下是正文&#xff01; 一、写文背景 我们在写后端…