mirror of
https://github.com/heqin-zhu/algorithm.git
synced 2024-03-22 13:30:46 +08:00
310 lines
11 KiB
TeX
310 lines
11 KiB
TeX
%\document{article}
|
||
\documentclass[UTF8]{ctexart}
|
||
\usepackage{amsmath}
|
||
\usepackage{graphicx}
|
||
\usepackage{fancyhdr}
|
||
\usepackage{float}
|
||
\usepackage{listings}
|
||
\title{Huffman编码压缩}
|
||
\author{朱河勤 PB16030899}
|
||
\pagestyle{fancy}
|
||
\lfoot{朱河勤 PB16030899}
|
||
\chead{Huffman编码压缩}
|
||
\rfoot{2017/11/11}
|
||
\begin{document}
|
||
\maketitle
|
||
|
||
\tableofcontents
|
||
\section{需求分析}
|
||
|
||
|
||
\paragraph{1}
|
||
正确创建huffman 树,并得到huffman编码
|
||
\paragraph{2}
|
||
压缩编码文件,使一般的文本文件变小,二进制文件可能不会变小
|
||
\paragraph{3}
|
||
解压文件,注意生成的文件要考虑同名,所以要考虑文件名的操作
|
||
\paragraph{4}
|
||
压缩解压时显示进度条
|
||
\paragraph{5}
|
||
解压完毕,统计文件数,压缩率,压缩时间等等
|
||
|
||
|
||
\section{概要设计}
|
||
\subsection{结点node}
|
||
ADT linkList\{\\
|
||
\paragraph{数据对象}:key , val, visited ,left,right \\
|
||
\paragraph{数据关系}:属性集合\\
|
||
\paragraph{基本操作}:
|
||
\subparagraph{node()} 新建一个结点
|
||
\subparagraph{node( const node\& nd} 复制一个结点
|
||
\subparagraph{operator<}重载操作符<
|
||
\}
|
||
\subsection{huffman树}
|
||
ADT linkList\{\\
|
||
\paragraph{数据对象}:root , encode\_map, total\\
|
||
\paragraph{数据关系}:属性集合\\
|
||
\paragraph{基本操作}:
|
||
\subparagraph{huffman( map)}传递字符的频率信息以map形式, 然后建立huffman树
|
||
\subparagraph{encode(string)} 传递文件名,压缩
|
||
\subparagraph{decode(string)} 传递文件名,解压缩
|
||
\subparagraph{preorder()}先序遍历,获得huffman编码
|
||
\subparagraph{display}显示所有信息,包括结点信息,以及huffman编码
|
||
\}
|
||
|
||
|
||
\section{详细设计}
|
||
\subsection{编码,解码算法}
|
||
\begin{lstlisting}[language=c++]
|
||
template<typename ky,typename wt>
|
||
void huffman<ky,wt>::preOrder(node<ky, wt>* nd,string s)
|
||
{
|
||
if(nd->left == NULL){
|
||
encode_map[nd->key] =s;
|
||
decode_map[s] = nd->key;
|
||
delete nd;
|
||
return ;
|
||
}
|
||
preOrder(nd->left,s+'0');
|
||
preOrder(nd->right,s+'1');
|
||
delete nd;
|
||
}
|
||
template<typename ky,typename wt>
|
||
string huffman<ky,wt>::decode(string zipfile_name)
|
||
{
|
||
string uniFileName(string);
|
||
FILE * src = fopen(zipfile_name.c_str(),"rb");
|
||
char file_name[nameLength];
|
||
fgets(file_name,nameLength,src);
|
||
int ct=-1;
|
||
while(file_name[++ct]!='\n');
|
||
int pos = zipfile_name.find('.');
|
||
if(pos==string::npos)pos=zipfile_name.size();
|
||
string name(zipfile_name.substr(0,pos)) ,suffix(file_name,file_name+ct),file(name+suffix);
|
||
file=uniFileName(file);
|
||
cout<<"extracting compressed file :"<<file<<endl;
|
||
FILE * f = fopen(file.c_str(),"wb");
|
||
if(!f){
|
||
cerr<<"Error! Maybe the file doesn't exist!"<<endl;cerr<<"open error!"<<endl;
|
||
abort( );
|
||
}
|
||
char t[numDigit];
|
||
fgets(t,numDigit,src);
|
||
int sz=atoi(t);
|
||
char code[sz];
|
||
fread(code,sz,1,src);
|
||
cout<<code<<endl;
|
||
cout<<"SZ"<<sz<<endl;
|
||
// return string();
|
||
map<string,char> mp;
|
||
int idx=0;
|
||
for(int i =0;i<sz;++i ){
|
||
if(code[i]==' '){
|
||
mp[string(code+idx,code+i)]=code[++i];
|
||
idx=i+1;
|
||
}
|
||
}
|
||
mapprint(mp);
|
||
char buff[once],*p;
|
||
string res;
|
||
while(!feof(src)){
|
||
p=buff;
|
||
int cur=0;
|
||
while(cur<once){
|
||
ToEightBin(p,fgetc(src));
|
||
}
|
||
cout<<buff;
|
||
return string();
|
||
while(buff[++cur]!='\0');
|
||
for(int i =0;i<cur;++i){
|
||
res.append(1,buff[i]);
|
||
if(mp.count(res)!=0){
|
||
fputc(mp[res],f);
|
||
res.clear();
|
||
}
|
||
}
|
||
}
|
||
fclose(src);
|
||
fclose(f);
|
||
}
|
||
|
||
template<typename ky,typename wt>
|
||
string huffman<ky,wt>::encode(string file_name)
|
||
{
|
||
string uniFileName(string);
|
||
int pos =file_name.rfind('.');
|
||
if(pos==string::npos)pos=file_name.size();
|
||
string zipfile = file_name.substr(0,pos)+string(".zzip");
|
||
zipfile = uniFileName(zipfile);
|
||
cout<<"generating zip file :"<<zipfile<<endl;
|
||
FILE * dst = fopen(zipfile.c_str(),"wb");
|
||
FILE * f = fopen(file_name.c_str(),"rb");
|
||
fputs(file_name.substr(pos).c_str(),dst);
|
||
fputc('\n',dst);
|
||
string data;
|
||
for(class map<string,ky>::iterator i=decode_map.begin();i!=decode_map.end() ;++i ){
|
||
data.append((i->first));
|
||
data.append(" ");
|
||
data+=(i->second);
|
||
}
|
||
cout<<data<<endl<<"SZ"<<data.size()<<endl;;
|
||
// mapprint( encode_map);
|
||
// return string();
|
||
int data_size = data.size(); // calculate the size of the code_data
|
||
char sz[numDigit];
|
||
itoa(data_size,sz,numDigit);
|
||
int ct=0;
|
||
for(;sz[ct];++ct)fputc(sz[ct],dst);
|
||
fputc('\n',dst);
|
||
// cout<<data<<data_size<<endl;
|
||
fwrite(data.c_str(),data_size,1,dst);
|
||
int sum=0,digit=0,num;
|
||
char src[once];
|
||
while(!feof(f)){
|
||
num = fread(src,sizeof(char),once,f);
|
||
string tmp;
|
||
for(int i =0;i<num;++i){
|
||
tmp=encode_map[src[i]];
|
||
for(int j =0;j<tmp.size();++j){
|
||
if(tmp[j])sum += 1<<(digit%8);
|
||
++digit;
|
||
if(digit==8){
|
||
fputc(sum,dst);
|
||
digit=sum=0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if(digit!=0){ //mark
|
||
fputc(sum,dst);
|
||
fputc(digit,dst);
|
||
}
|
||
else fputc(0,dst);
|
||
fclose(f);
|
||
fclose(dst);
|
||
return zipfile;
|
||
}
|
||
\end{lstlisting}
|
||
\subsection{输出进度条}通过输出@以显示当天进度,提示用户
|
||
\subsection{改变文件名}由于当前工作目录可能有解压或压缩后的同名文件,要进行处理,我的方法是在后缀前,文件名后加上(n) 其中,n为数字
|
||
\subsection{统计信息}处理后,统计信息,包括压缩的文件名目录,所用的时间,文件大小,压缩率等等
|
||
|
||
\section{调试分析}
|
||
\subsection{编码信息的存储}
|
||
我先计算了编码信息的大小,然后存在文件中,以键值对的形式,如''001 k''再存储编码信息,这样就能更快的读取,并能节省空间
|
||
\subsection{最后一个字节}
|
||
由于储存时一char八位一次地储存,所以最后一字节可能凑不齐,我通过补0地方式,并在最后再增加一字节记录补0的个数来解决
|
||
\subsection{文件名的处理}
|
||
文件名要要记录后缀以识别文件种类,以及文件是否存在,能否打开,生成的文件是否重名。比如,当发现重名时,你要生成(n)形式,但可能(n)也已经存在,所以要一直检查(1),(2)...检查到一个较大的数,或者是,第一次未出现时,才能确定N
|
||
|
||
|
||
\section{用户手册}
|
||
本程序需要在windows环境下运行,可以直接打开.exe文件,或者在命令行,支持命令行参数\
|
||
下面为程序运行界面
|
||
\begin{verbatim}
|
||
C:\Users\mbinary\Desktop\dataStructrue\huffman
|
||
input file names separated by space
|
||
OR press enter to use the default file:初心未改.mp4
|
||
|
||
|
||
opening 初心未改.mp4...
|
||
the file 初心未改.zzip already exists! continue? [Y/n]:generating zip file :初心未改(2).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
compress 初心未改.mp4 successfully
|
||
|
||
continue to uncompress? [Y/n]
|
||
|
||
the file 初心未改(2).mp4 already exists! continue? [Y/n]:
|
||
extracting compressed file :初心未改(2).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
get 初心未改(3).mp4 successfully
|
||
|
||
dealt file number 1
|
||
初心未改.mp4 18891ms 8274.84KB/8296.13KB :99.74%
|
||
\end{verbatim}
|
||
|
||
\section{测试结果}
|
||
\subsection{文本文件}
|
||
输入了几个不存在的文件,检查强壮性。同时发现文本文件压缩率还是很令人满意的
|
||
\begin{verbatim}
|
||
input file names separated by space
|
||
OR press enter to use the default file:初心未改.mp4
|
||
em cvem ven em.cc
|
||
|
||
opening em...
|
||
Path Error! Opening em Failed
|
||
opening cvem...
|
||
Path Error! Opening cvem Failed
|
||
opening ven...
|
||
Path Error! Opening ven Failed
|
||
opening em.cc...
|
||
the file em.zzip already exists! continue? [Y/n]:
|
||
generating zip file :em(2).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
compress em.cc successfully
|
||
|
||
continue to uncompress? [Y/n]
|
||
|
||
the file em(2).cc already exists! continue? [Y/n]:
|
||
extracting compressed file :em(2).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
get em(3).cc successfully
|
||
|
||
dealt file number 4
|
||
em.cc 2266ms 4.69KB/7.57KB :61.87%
|
||
\end{verbatim}
|
||
\subsection{二进制文件1}
|
||
几乎没有压缩,原因应该是.exe文件本来就压缩得很好了,几乎已经是随机的编码了,所以huffman没有效果,这也是文件不能重复压缩直至没有大小的原因。
|
||
\begin{verbatim}
|
||
input file names separated by space
|
||
OR press enter to use the default file:初心未改.mp4
|
||
GitHubDesktopSetup.exe
|
||
|
||
opening GitHubDesktopSetup.exe...
|
||
the file GitHubDesktopSetup.zzip already exists! continue? [Y/n]:
|
||
generating zip file :GitHubDesktopSetup(2).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
compress GitHubDesktopSetup.exe successfully
|
||
|
||
continue to uncompress? [Y/n]
|
||
|
||
the file GitHubDesktopSetup(2).exe already exists! continue? [Y/n]:
|
||
extracting compressed file :GitHubDesktopSetup(2).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
get GitHubDesktopSetup(3).exe successfully
|
||
|
||
dealt file number 1
|
||
GitHubDesktopSetup.exe 309610ms 81718.46KB/81718.46KB :100.00%
|
||
\end{verbatim}
|
||
\subsection{二进制文件2}
|
||
\begin{verbatim}
|
||
input file names separated by space
|
||
OR press enter to use the default file:初心未改.mp4
|
||
|
||
|
||
opening 初心未改.mp4...
|
||
the file 初心未改.zzip already exists! continue? [Y/n]:generating zip file :初心未改(3).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
compress 初心未改.mp4 successfully
|
||
|
||
continue to uncompress? [Y/n]
|
||
|
||
the file 初心未改(3).mp4 already exists! continue? [Y/n]:
|
||
extracting compressed file :初心未改(3).zzip
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
get 初心未改(4).mp4 successfully
|
||
|
||
dealt file number 1
|
||
初心未改.mp4 13688ms 8274.84KB/8296.13KB :99.74%
|
||
\end{verbatim}
|
||
|
||
\clearpage
|
||
\end{document} |