#include<stdio.h>
#include <stdlib.h>
#include<dir.h>
#include<string.h>
#define MAX 512
struct Inode //文件结构结点
{
struct Inode *leftPtr;
char name[MAX_PATH]; //存储文件或文件夹名称
int isdir; //文件属性标志位
unsigned lenth; //文件长度
int hasc_; //当此节点有孩子时为1,否则为0
int hasb_; //当此节点有兄弟时为1,否则为0
struct Inode *rightPtr;
};
typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree; //动态分配数组存储赫夫曼树
typedef char **HuffmanCode; //动态分配数组存储赫夫曼编码表
struct code_tree //编码树节点,存储字符的ascii码
{
unsigned int Subscript;
struct code_tree *leftchild,*rightchild;
};
void insertNode(struct Inode *treeNode,int i,char []); //构造二叉树-孩子兄弟链
struct Inode * search(const char * dir); //遍历文件夹
void preOder(struct Inode *Root); //前序遍历文件树并将文件内容写入Var文件,建立Var文件结构区数组
void Unvar(); //解压Var文件
void search_struct(const char *,FILE *); //遍历结构数组恢复原文件
void free_tree(struct Inode *); //释放 动态二叉树
void freetree(struct code_tree*);
void compress(); //文件压缩
void HuffmanCoding(HuffmanTree *HT,HuffmanCode *HC,int w[],int n);//赫夫曼编码
void Select(HuffmanTree H_T,int ,int *,int *); //选择频率最少的两个节点建立赫夫曼树
unsigned char code_convert(char a[]); //压缩时将8位编码转化为一个字符
void code_disconvert(unsigned char a[],unsigned char b); //解压时将一个字符转化为0,1字符串
void discompress(); //解压函数
struct code_tree * createcodetree(); //解压时建立编码树
struct code_tree * search_codetree(struct code_tree * cur_rent,unsigned char code_b[],int *c);
//解压时遍历编码树
static unsigned fillength=0; //Var文件数据区长度
static struct Inode *root=NULL,*currentPtr=NULL; //文件树根节点
static struct Inode Struct[1000],Struct_Node[1000]; //Var文件结构区数组
static int num=0,num1,char_num[257]={0},node_num=0;
FILE *filePtr,*var_Ptr;
HuffmanTree h_t;
main()
{
struct Inode *NodePtr=NULL;
char path[MAX_PATH];
int id=0;
while(id!=2){ //循环输入要压缩的文件及文件夹,2结束输入
if(root==NULL) //判断并建立根节点,根节点为定义为Unvar文件夹,包括所有要压缩的文件
{
root=malloc(sizeof(struct Inode));
if(root!=NULL)
{
root->leftPtr=NULL;
strncpy(root->name,"Unvar",7);
root->isdir=0; //标志位0表示文件夹
root->lenth=0;
root->rightPtr=NULL;
}
currentPtr=root;
}
else
{
printf("输入要压缩文件属性(1代表文件,0代表文件夹,2结束输入):");
scanf("%d",&id);
while(id!=2)
{ //输入文件属性调用insertNode函数将文件或文件夹 插入文件树
if(id==1)
{
printf("输入文件路径: ");
scanf("%s",path);
NodePtr=malloc(sizeof(struct Inode));
if(NodePtr!=NULL)
insertNode(NodePtr,id,path);
}
else
{
printf("输入文件夹路径: ");
scanf("%s",path);
NodePtr=malloc(sizeof(struct Inode));
if(NodePtr!=NULL)
insertNode(NodePtr,id,path);
}
printf("输入要压缩文件属性(1代表文件,0代表文件夹,2结束输入):");
scanf("%d",&id);
}
}}
if((filePtr=fopen("Hold.Var","wb+"))==NULL) //建立Var文件
printf("无法建立Holde文件\n");
else
compress(); //压缩文件
num1=num;
discompress(); //解压文件
Unvar();
system("pause");
return 0;
}
void insertNode(struct Inode *treeNode,int i,char path1[])
{
treeNode->leftPtr=NULL; //初始化文件结点
strncpy(treeNode->name,path1,255); //写入文件名
treeNode->isdir=i; //1表示文件,0表示文件夹
treeNode->lenth=0;
treeNode->rightPtr=NULL;
if(currentPtr==root) //将结点插入左子树或右子树
currentPtr->leftPtr=treeNode;
else
currentPtr->rightPtr=treeNode;
currentPtr=treeNode;
if(i==0) //如果结点时文件夹调用函数遍历将头结点返回
currentPtr->leftPtr=search(currentPtr->name);
}
/*搜索文件夹生成文件树,返回root1tr*/
struct Inode *search(const char * dir)
{
struct _finddata_t ffblk;
char path[MAX_PATH*2];
long filehandle;
int finish=0;
struct Inode *current, *last,*root1;
int firstFile = 1;
current=last= NULL;
sprintf(path,"%s\\*.*",dir); //匹配条件,搜索所有文件
filehandle = _findfirst(path,&ffblk); //是否找到第一个文件 找到返回0 未找到返回-1
if (filehandle == -1)
return NULL;
while (!(finish=_findnext(filehandle,&ffblk)))
{
if (!strcmp(ffblk.name,"..")||!strcmp(ffblk.name,".")) //忽略..文件夹或.文件夹
{
root1=NULL; //压缩空文件夹关键
continue;
}
current=malloc(sizeof(struct Inode));
if(current!=NULL)
{
current->leftPtr=current->rightPtr=NULL;
}
if (firstFile == 1) //找到第一个文件作为根结点返回
{
root1 = last = current;
firstFile++;
}
else //否则作为上一结点的兄弟插入
{
last->rightPtr = current;
}
char subPath[MAX_PATH]; //将文件路径和名称写入结点
sprintf(subPath,"%s\\%s",dir,ffblk.name);
strncpy(current->name,subPath,255);
if ( (_A_SUBDIR & ffblk.attrib) && strcmp(ffblk.name,"..") && strcmp(ffblk.name,"."))
{ //正常的文件夹
current->isdir = 0;
current->leftPtr = search(subPath);
current->rightPtr = NULL;
last = current;
}
else if (strcmp(ffblk.name, ".") && strcmp(ffblk.name, "..") )
{ //文件
current->isdir = 1;
current->leftPtr = NULL;
last = current;
}
else
printf("错误文件");
}
return root1;
}
void preOder(struct Inode *Root) //前序遍历二叉树将文件写入Var文件
{
FILE *file_ptr;
if(Root!=NULL)
{
if(Root->leftPtr!=NULL) //将结点是否有孩子兄弟写入标志位
Root->hasc_=1;
else
Root->hasc_=0;
if(Root->rightPtr!=NULL)
Root->hasb_=1;