哈夫曼树编码压缩解压缩

闽南师范大学
《数据结构》课程设计报告
课程设计题目: Huffman 编码压缩和解压缩文件工具设计
摘 要
Huffman 编码将给字母分配编码。每个字母的编码的长度取决于
在被压缩文件中对应字母的出现频率,我们称之为 权重 。每个字母的
Huffman 编码是从称为 Huffman 编码树 满二叉树 (所有节点要么有
左右两个子孩子,要么就没有子孩子)中得到的。 Huffman 编码树
每一个叶节点对应于一个字母,叶节点的权重 (weight)就是它对
应的字母出现的频率。使用权重的目的是建立的 Huffman 编码树
小外部路径权重 。 通过对被压缩文件建立Huffman树得到对应字符的Huffman编码
再对文件进行转码再以 16 进制的方式写入解压后的文件中达到压缩
的目的,通过解压后的文件以及 Huffman 编码文件,反过来建立
Huffman 树得到 Huffman 编码进行解压。
关键词 Huffman ,树, 16 进制,压缩,解压 目录
摘要 .............................................................................................. 2
1 问题背景 ................................................................................. 4
2 设计内容 ................................................................................. 4
2.1 问题描述 ..................................................................... 4
2.2 需求分析 ..................................................................... 4
3 整体设计 ................................................................................. 5
3.1 系统整体功能图 ....................................................... 5
3.2 Huffman 树编码解码大致流程 ............................. 5
3.3 Huffman 树的建立 .................................................... 6
4 详细设计 ................................................................................. 7
4.1 压缩的设计 ........................................................................... 7
4.2 解压的设计 ........................................................................... 12
5 设计感受 ................................................................................... 13
附录 ................................................................................................ 13
-3- 1 问题背景
许多文件过于庞大占用过多的存储空间,通过压缩来使文件变小,
需要使用时再将文件解压使用,大大的缓解了存储空间不足的问题,
减少数据大小以节省保存空间和传输的时间,有助于释放多余的存储
空间。日常生活中也难免会遇到由于文件体积过大而无法发送的问题,
压缩文件则可以大大提高我们的工作效率。
2 设计内容
2.1 问题描述
设计该系统主要是为了解决文件过大的问题,提供压缩的工具以解决
问题,需要使用时再通过这个工具进行解压。
2.2 需求分析
1 、输入一个待压缩的文本文件路径名,统计文本文件中各字符的个
数作为权值,生成哈夫曼树,生成编码文件;
2 、输入一个待解压的压缩文件路径名称,并利用该压缩文件得到相
应的哈夫曼树,再将编码序列译码得到解码文本文件;
3 、在压缩时输出哈夫曼树中度为 2 的节点个数;
-4- 3 整体设计
3.1 系统整体功能图
3.2Huffman 树编码解码大致流程
编码:
1. 读入待编码源文件;
2. 第一次扫描:统计文件中各字符的出现频率;
3. 构建 Huffman 树;
4. 遍历 Huffman 树,获得各字符的码表;
5. 第二次扫描:对源文件的每个字符编码;
-5- 解码:
1. 读入编码后的文件;
2. 获取 Huffman 树;
3. 从根节点开始依据从文件中读取的 Huffman 码值沿树行走,至叶结点时完成一个字符的解码,
并返回根节点;
4. 重复上述过程,完成所有字符的解码;
3.3 Huffman 树的建立
1. 建立 Huffman 树的节点,一个节点有 lchild rchild parent weight flag
等参数。
class HTNode:
def __init__(self,d="",w=None):
self.data=d
self.weight=w
self.parent=-1
self.lchild=-1
self.rchild=-1
self.flag=True
2. 统计文本字符出现的次数作为每个字符的权值,每次将字符权值最小的取出
建立 Huffman 树然后再添加到原数组当中,直到所有字符都取出, Huffman 树就
建立好。
-6- 4 详细设计
4.1 压缩的设计
1. 统计文本字符出现的次数作为每个字符的权值,每次将字符权值最小的取出
建立 Huffman 树然后再添加到原数组当中,直到所有字符都取出, Huffman 树就
建立好。
def weight(self,txt):
global ht,n,D,W
D=[]
W=[]
j=0
Str =str(txt)
resoult = {}
for i in Str:
resoult[i] = Str.count(i)
for key in resoult:
D.append(key)
W.append(resoult[key])
j+=1
n=j
def Creat(self):
global ht,n,D,W,k1
heap=[]
ht=[None]*(2*n-1)
for i in range(n):
heapq.heappush(heap,[W[i],i])
ht[i]=HTNode(D[i],W[i])
for k in range(n,2*n-1):
p1=heapq.heappop(heap)
p2=heapq.heappop(heap)
ht[k]=HTNode()
ht[k].weight=ht[p1[1]].weight+ht[p2[1]].weight
ht[p1[1]].parent=k
ht[k].lchild=p1[1]
ht[p1[1]].flag=True
ht[p2[1]].parent=k
ht[k].rchild=p2[1]
ht[p2[1]].flag=False
heapq.heappush(heap,[ht[k].weight,k])
-7- 2. 先定义一个 count=0 ,遍历 Huffman 树所有节点如果一个节点的左孩子和右孩
子都不为 None count 则加 1 。最后遍历完后输出 count
k1=k
count=0
for y in range(int(1+k1/2),k1):
if ht[y].lchild!=None:
if ht[y].rchild!=None:
print(ht[y].weight)
count+=1
print(" 度为 2 的节点有: ")
print(count)
3. Huffman 树建立好了之后,将左子树分支编码为 0 ,右子树分支编码为 1 ,每
个字符都得到固定的 01 编码。经过这个编码设置之后我们可以发现,出现频率
越高的字符越会在上层,这样它的编码越短;出现频率越低的字符越会在下层,
编码越短。经过这样的设计,最终整个文本存储空间才会最大化的缩减。
def CreateH(self):
global n,ht,hcd
hcd=[]
for i in range(n):
conum=[]
j=i
while ht[j].parent!=-1:
if ht[j].flag:
conum.append('0')
else:
conum.append('1')
j=ht[j].parent
conum.reverse()
hcd.append(''.join(conum))
print(hcd)
4. 把文件内容对应编码表对应的 01 编码得到对应的一整个文本的 01 编码,将
01 编码 8 个为一个单位转化成相应的 byte 。把 Huffman 树的信息以二进制的方
式写入文件,把转化成的 byte 以二进制的方式写入文件,压缩完成。
def zuhe(self,txt,wj):
global kd
by=''
de=[]
-8- -9-
num=0
file=open(wj+'yasuo.txt',"wb")
global hcd,D,W
for i in txt:
for j in range(len(D)):
if i==D[j]:
by=by+hcd[j]
ti=len(by)
if ti%8!=0:
t1=ti%8
by=by+'0'*(8-t1)
kd=1
head=max(W)
if head> 255:
kd = 2
if head > 65535:
kd = 3
if head > 16777215:
kd = 4
while 1:
temp=hex(int(by[num:num+8],2))
num=num+8
de.append(temp)
if num==len(by):
break
file.write(int.to_bytes(len(D) ,2 ,byteorder = 'big'))
# 写出结点数量
file.write(int.to_bytes(kd,1 ,byteorder = 'big'))
for x in range(len(D)):
file.write(D[x].encode())
file.write(int.to_bytes(W[x],kd,byteorder = 'big'))
for li in de:
s = struct.pack('B',int(li,16))
file.write(s) -10-
5. 压缩成功占用空间减小
4.2 解压的设计
1. 根据文件头的信息得到 Huffman 树的基本信息,重建 Huffman
class jieya:
def Creat2(self,wj):
global D1,W1,lis,j,n,ht
D1=[]
W1=[]
j=1
f=open(wj,"rb")
f.seek(0,2)
f.seek(0)
n = int.from_bytes(f.read(2), byteorder = 'big')
# 取出结点数量
bit_width = int.from_bytes(f.read(1), byteorder = 'big')
i=0
while i < n:
D1.append(f.read(1).decode())
W1.append(int.from_bytes(f.read(bit_width), byteorder = 'big'))
i+=1 print(D1)
print(W1)
heap=[]
ht=[None]*(2*n-1)
for i in range(n):
heapq.heappush(heap,[W1[i],i])
ht[i]=HTNode(D1[i],W1[i])
for k in range(n,2*n-1):
p1=heapq.heappop(heap)
p2=heapq.heappop(heap)
ht[k]=HTNode()
ht[k].weight=ht[p1[1]].weight+ht[p2[1]].weight
ht[p1[1]].parent=k
ht[k].lchild=p1[1]
ht[p1[1]].flag=True
ht[p2[1]].parent=k
ht[k].rchild=p2[1]
ht[p2[1]].flag=False
heapq.heappush(heap,[ht[k].weight,k])
3. 重新对每个字符编码,将左子树分支编码为 0 ,右子树分支编码为 1 ,得到每
个字符的 01 编码。
hcd1=[]
for i in range(n):
code=[]
j=i
while ht[j].parent!=-1:
if ht[j].flag:
code.append('0')
else:
code.append('1')
j=ht[j].parent
code.reverse()
hcd1.append(''.join(code))
4. 文件内容以二进制读出,对照编码表,得到编码对应的原字符,直到所有的
huffman 编码都比较完,完成解压缩还原文件原内容。
Q=''
for i in f.read():
i='{:0>8}'.format(str(bin(i))[2:])
Q=Q+i
code = ""
ans = ""
-11- for ch in Q:
code += ch
for j in range(len(hcd1)):
if code==hcd1[j]:
code = ""
ans +=str(D1[j])
break
f=open(wj+'jieya.txt',"wb")
f.write(ans.encode("utf-8"))
5. 解压成功还原成原文件。
5. 设计感受
通过 Huffman 树编码得到的文件占用空间大大减少,对于压缩文件十分适用,
通过这份课设我对于 Huffman 的理解更加深刻,对 Huffman 树的构造使用更加
了解,更加能理解 Huffman 编码会成为一种非常高效,压缩率高的编码方式的原
因。对 Huffman 树的构造使用更加了解,这是一个非常有实际效益,非常有意义
的一个压缩和解压的工具,代码上还有很多地方可以进行完善,但是这份课设真
的让我收益匪浅。感谢老师提出的更多的要求,让我克服很多困难完成这份课设。
附录 代码
import heapq
import struct
import sys
sys.setrecursionlimit(1000000)
-12- class HTNode:
def __init__(self,d="",w=None):
self.data=d
self.weight=w
self.parent=-1
self.lchild=-1
self.rchild=-1
self.flag=True
class node:
def weight(self,txt):
global ht,n,D,W
D=[]
W=[]
j=0
Str =str(txt)
resoult = {}
for i in Str:
resoult[i] = Str.count(i)
for key in resoult:
D.append(key)
W.append(resoult[key])
j+=1
n=j
def Creat(self):
global ht,n,D,W,k1
heap=[]
ht=[None]*(2*n-1)
for i in range(n):
heapq.heappush(heap,[W[i],i])
ht[i]=HTNode(D[i],W[i])
for k in range(n,2*n-1):
p1=heapq.heappop(heap)
p2=heapq.heappop(heap)
ht[k]=HTNode()
ht[k].weight=ht[p1[1]].weight+ht[p2[1]].weight
ht[p1[1]].parent=k
ht[k].lchild=p1[1]
ht[p1[1]].flag=True
ht[p2[1]].parent=k
ht[k].rchild=p2[1]
ht[p2[1]].flag=False
-13- heapq.heappush(heap,[ht[k].weight,k])
k1=k
e=0
for y in range(int(1+k1/2),k1):
if ht[y].lchild!=None:
if ht[y].rchild!=None:
print(ht[y].weight)
e+=1
print(" 度为 2 的节点有: ")
print(e)
def CreateH(self):
global n,ht,hcd
hcd=[]
for i in range(n):
conum=[]
j=i
while ht[j].parent!=-1:
if ht[j].flag:
conum.append('0')
else:
conum.append('1')
j=ht[j].parent
conum.reverse()
hcd.append(''.join(conum))
def zuhe(self,txt,wj):
global kd
by=''
de=[]
num=0
file=open(wj+'yasuo.txt',"wb")
global hcd,D,W
for i in txt:
for j in range(len(D)):
if i==D[j]:
by=by+hcd[j]
ti=len(by)
if ti%8!=0:
t1=ti%8
by=by+'0'*(8-t1)
kd=1
-14- -15-
head=max(W)
if head> 255:
kd = 2
if head > 65535:
kd = 3
if head > 16777215:
kd = 4
while 1:
temp=hex(int(by[num:num+8],2))
num=num+8
de.append(temp)
if num==len(by):
break
file.write(int.to_bytes(len(D) ,2 ,byteorder = 'big'))
# 写出结点数量
file.write(int.to_bytes(kd,1 ,byteorder = 'big'))
for x in range(len(D)):
file.write(D[x].encode())
file.write(int.to_bytes(W[x],kd,byteorder = 'big'))
for li in de:
s = struct.pack('B',int(li,16))
file.write(s)
def ya(self,txt,wj):
self.weight(txt)
self.Creat()
self.CreateH()
self.zuhe(txt,wj)
print(" 压缩成功! ")
class jieya:
def Creat2(self,wj):
global D1,W1,lis,j,n,ht
D1=[]
W1=[]
j=1
f=open(wj,"rb")
f.seek(0,2)
f.seek(0)
n = int.from_bytes(f.read(2), byteorder = 'big')
# 取出结点数量
bit_width = int.from_bytes(f.read(1), byteorder = 'big')
i=0
while i < n:
D1.append(f.read(1).decode())
W1.append(int.from_bytes(f.read(bit_width), byteorder = 'big'))
i+=1 print(D1)
print(W1)
heap=[]
ht=[None]*(2*n-1)
for i in range(n):
heapq.heappush(heap,[W1[i],i])
ht[i]=HTNode(D1[i],W1[i])
for k in range(n,2*n-1):
p1=heapq.heappop(heap)
p2=heapq.heappop(heap)
ht[k]=HTNode()
ht[k].weight=ht[p1[1]].weight+ht[p2[1]].weight
ht[p1[1]].parent=k
ht[k].lchild=p1[1]
ht[p1[1]].flag=True
ht[p2[1]].parent=k
ht[k].rchild=p2[1]
ht[p2[1]].flag=False
heapq.heappush(heap,[ht[k].weight,k])
global hcd1
hcd1=[]
for i in range(n):
code=[]
j=i
while ht[j].parent!=-1:
if ht[j].flag:
code.append('0')
else:
code.append('1')
j=ht[j].parent
code.reverse()
hcd1.append(''.join(code))
Q=''
for i in f.read():
i='{:0>8}'.format(str(bin(i))[2:])
Q=Q+i
code = ""
ans = ""
for ch in Q:
code += ch
for j in range(len(hcd1)):
if code==hcd1[j]:
-16- code = ""
ans +=str(D1[j])
break
f=open(wj+'jieya.txt',"wb")
f.write(ans.encode("utf-8"))
print(" 欢迎使用! ")
print("1. 压缩 ")
print("2. 解压 ")
print("0. 退出 ")
b=int(input(" 请选择 :"))
while 1:
if b==1:
wj=input(" 请输入要压缩的文件路径 :").strip()
t=open(wj,'r',encoding='UTF-8')
txt=t.read()
t.close()
a=node()
a.ya(txt,wj)
b=int(input(" 请选择 :"))
if b==2:
wj=input(" 请输入要解压的文件路径 :").strip()
f=open(wj,"rb")
file=f.read()
a=jieya()
a.Creat2(wj)
f.close()
print(" 解压成功 !")
b=int(input(" 请选择 :"))
if b==0:
break
-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值