精华内容
下载资源
问答
  • 数据结构

    2012-09-10 14:48:39
    12. 以下数据结构中哪一个是非线性结构?( ) A. 队列 B. 栈 C. 线性表 D. 二叉树 13. 树最适合用来表示( )。 A.有序数据元素 B.无序数据元素 C.元素之间具有分支层次关系的数据 D.元素之间无联系的数据 14. ...
  • 数据结构(C++)有关练习题

    热门讨论 2008-01-02 11:27:18
    在计算机科学发展过程中,早期数据结构教材大都采用PASCAL语言描述工具,后来出现了采用C语言描述工具的教材版本、至今又出现了采用C++语言描述工具的多种教材版本。本教实验指导书是已经学习过C++语言的...
  • 哈夫曼树和哈夫曼编码应用之图片压缩编码c++实现

    千次阅读 多人点赞 2018-12-07 22:38:04
    本人正在学习数据结构,在前几天压缩图片的项目,感觉到有必要分享给大家。因此今天我就分享给大家c语言数据结构有关哈夫曼树压缩图片的项目实现。   一:下面先介绍有关的知识: 1.背景 压缩软件是用特定...

    本人正在学习数据结构,在前几天做了压缩图片的项目,感觉到有必要分享给大家。因此今天我就分享给大家c语言数据结构有关哈夫曼树压缩图片的项目实现。

     

    一:下面先介绍有关的知识:
    1.背景

    压缩软件是用特定算法压缩数据的工具,压缩后的文件称为压缩包,可以对其进行解压。那么为什么要用到压缩软件呢?我们都知道,文件是用编码进行存储的,编码要用到字节,而不可避免的一个文件中会出现很多重复的字节,用压缩软件可以减少重复的字节,方便在互联网上实现更快的传输,也可以减少文件在磁盘上的占用空间,常见的压缩软件有rar,zip等。

    压缩可以分为无损压缩和有损压缩两种,无损压缩后的文件,经过解压后可以完全恢复到之前的文件,rar,zip等格式都是无损压缩格式,而图片文件jpg,音乐文件mp3都是有损压缩格式。

     

    2.编码介绍

    计算机文件是由一个个字节组成的,一个字节有8位二进制编码构成,共有0-255种可能的情况。由于文件中的字节可能会重复出现,可以对不同的字节设计长度不等的编码,让出现次数较多的字节,采用较短的编码,这样可以使文件编码的总长度减少。

     

    3.哈夫曼树和哈夫曼编码

    (1)哈夫曼树

    有关二叉树的知识这里就不讲解了,大家可以自行学习。这里我们统计文件中256种字节重复的次数作为权值,构造一棵有256个叶节点的二叉树,如果带权路径长度最小,我们称为这样的二叉树为最有二叉树,也叫哈夫曼(Huffman)树。

    (2)哈夫曼编码

    哈夫曼树从根结点到每个叶子结点都有一条路径,对路径上的分支,约定指向左子树的为0,指向右子树的为1,从根到每个叶子结点路径上的0和1构成的序列就是这个叶节点的哈夫曼编码。

    如图所示:

    这时编码就是:

    A:000  B:001  C:010 D:011 E:11

    使用哈夫曼编码给每个字节重新编码,重复次数较多的字节,哈夫曼编码较短,这样就比以前的二进制编码短了许多,因此可以实现压缩。

     

     

    这里我们实现把一个bmp格式的图片进行压缩编码。

    二:过程和代码实现与分析

    1.流程简述:

    (1)读取文件

    先读取文件,生成一棵带权二叉树。树在程序中可以使用顺序结构,链式结构两种方式实现,由于这棵带权二叉树的叶子节点有256个,存储空间是固定的,则这里可以使用顺序结构来表示二叉树。

    (2)定义二叉树结构

    定义一个结构体HaffNode来表示二叉树的叶子节点,记录每个结点的权值,标记,父结点,左孩子和右孩子。创建一个结构体数组来存储这棵带权二叉树的每个叶子结点的信息。

    (3)生成哈夫曼编码

    其次,生成Huffman树后,要生成哈夫曼编码,就要先遍历这棵二叉树,这里我用的是先序遍历方法,定义一个字符串数组Code来存储每个叶子结点的哈夫曼编码。

    (4)字符串转字节实现压缩

    )由于哈夫曼编码是以字符串数组的形式保存的,重新编码后的数据将是一个很长的字符串。定义Str2byte函数,将字符串转化为字节,才能转化为最终的编码,将其保存到*.huf中,则实现了文件压缩。

    (5)解压缩

    最后,为了保证压缩后的数据能够被正常解压,除了保存压缩的数据,还应保存原文件的长度和256种字节重复出现的次数。这时就需要定义一个文件头,用于保存这些信息,再保存压缩文件时,同时向文件中写入文件头和压缩数据,保证文件能够被还原。

     

    2.代码实现与分析

    (1)打开Microsoft Visual Studio 2010,创建一个解决方案,名字为"HuffmanSLN",在HuffmanSLN解决方案下面新建一个空的win32控制台工程,名字为"HfmCompressCPro"。

    (2)打开文件

    在源文件(Source Files)中新建"main.cpp"文件,作为程序运行的入口函数。

    导入<iostream>头文件,声明using bamespace std,使用cout输出信息,用cin接受键盘输入文件信息。

    代码如下:
     

    #include <iostream>
    #include <stdlib.h>
    using namespace std;
    
    int main()
    {
            cout<<"--------------Huffman文件压缩编码---------------"<<endl;
    	cout<<"请输入文件名:";
    	char filename[256];//文件名
    	cin>>filename;
            return 0;
    }

    (3)读取原文件

    以二进制流的方式,只读打开文件,一个个地逐个读取字节数据,统计文件中256种字节重复出现的次数,保存到一个数组中

    int weight[256]中,然后将扫描的结果在控制台打印下来。

    代码如下:

    #include <iostream>
    #include <stdlib.h>
    using namespace std;
    
    int main()
    {
            cout<<"--------------Huffman文件压缩编码---------------"<<endl;
    	cout<<"请输入文件名:";
    	char filename[256];//文件名
    	cin>>filename;
    
            char ch;
    	int weight[256]={0};
    
    	//以二进制流的方式打开文件
    	FILE* in = fopen(filename,"rb");
    	if(in == NULL)
    	{
    		printf("打开文件失败");
    		return 0;
    	}
    	//扫描文件,获得权重
    	while(ch = getc(in) != EOF)
    	{
    		weight[ch]++;
    	}
    	//关闭文件
    	fclose(in);
    
            //显示256个字节出现的次数
    	cout<<"Byte "<<"Weight"<<endl;
    	for(int i=0;i<256;i++)
    	{
    		printf("0x%02X %d\n",i,weight[i]);
    	}
    	system("pause");
            return 0;
    }

    这里我们的图片在E盘的根目录下:

    即下面的一张图:

    运行结果如下:

    (4)生成哈夫曼树

    在Haffman.h文件中,定义一个存储哈夫曼树结点信息的结构体,有权值,标记(若当前结点未加入结构体,flag=0;若当前结点加入结构体,flag=1),双亲结点下标,左孩子结点下标,右孩子结节下标。

    在Haffman.cpp文件中创建构造哈夫曼树的函数,在Haffman.h文件中声明。

    Haffman.h文件

    typedef struct
    {
    	int weight;		//权值
    	int flag;	    //标记
    	int parent;		//双亲结点下标
    	int leftChild;	//左孩子下标
    	int rightChild;	//右孩子下标
    }HaffNode;
    
    //创建叶结点个数为n,权值数组为weight的哈夫曼树haffTree
    void Haffman(int weight[],int n,HaffNode haffTree[]);

    Haffman.cpp文件

    #include "Huffman.h"
    #define MaxValue 10000
    
    //创建叶结点个数为n,权值数组为weight的哈夫曼树haffTree
    void Haffman(int weight[],int n,HaffNode haffTree[])
    {
    	int i,j,m1,m2,x1,x2;
    	//哈夫曼树haffTree初始化,n个叶结点的二叉树共有2n-1结点
    	for(i = 0;i < 2 * n - 1;i++)
    	{
    		if(i < n)	//如果是小于n的结点(叶子结点),则把每个字节的重复次数作为权值赋给这些该结点
    			haffTree[i].weight = weight[i];
    		else
    			haffTree[i].weight = 0;	//其他非叶子结点权值设为0
    		haffTree[i].parent = -1;
    		haffTree[i].flag   = 0;
    		haffTree[i].leftChild  = -1;
    		haffTree[i].rightChild = -1;
    	}
    
    	//构造哈夫曼树haffTree的n-1个非叶结点
    	for(i = 0;i < n - 1;i++)
    	{
    		//这里假设先进行一次循环,i=0,后面的代码注释方便大家理解
    		m1=m2=MaxValue;
    		x1=x2=0;
    		//找到权值最小和次小的子树,就是找到权值最小的两个结点
    		for(j = 0;j < n + i;j++)	//这里先让i=0;则对于n个叶子结点,按权值最小和次小的顺序连接结点生成哈夫曼树
    		{
    			//这里比较每个结点的权值,如果小于上一个结点(已查找的最小权值结点)权值且该结点没有被访问
    			if(haffTree[j].weight < m1 && haffTree[j].flag == 0)	
    			{
    				m2 = m1;	//令m2为上一个结点(非最小结点的前面的权值)的下标
    				x2 = x1;	//x2为上一个结点(非最小结点的前面的权值)下标
    				m1 = haffTree[j].weight;	//m1记录该结点的权值
    				x1 = j;						//x1为该结点的下标
    			}
    			//这里比较每个结点的权值,如果小于非最小结点的前面的结点权值且该结点没有被访问
    			else if(haffTree[j].weight < m2 && haffTree[j].flag == 0)		
    			{
    				m2 = haffTree[j].weight;	//m2记录该结点的权值
    				x2 = j;						//x2记录该结点的下标
    			}
    			//比较完所有的结点后,x1就为最小权值结点的下标,x2就为次小权值结点的下标
    		}
    
    		//将找出的两棵权值最小和次小的子树合并为一棵子树
    		haffTree[x1].parent = n + i;	//x1就为最小权值结点的下标
    		haffTree[x2].parent = n + i;	//x2就为次小权值结点的下标
    		haffTree[x1].flag   = 1;		//x1被访问
    		haffTree[x2].flag   = 1;		//x2被访问
    		haffTree[n+i].weight = haffTree[x1].weight + haffTree[x2].weight;
    		haffTree[n+i].leftChild = x1;	//左孩子存储结点x1
    		haffTree[n+i].rightChild = x2;	//右孩子存储结点x2
    	}
    }

    在main.cpp中编测试一下,改为如下形式:

     

    #include <iostream>
    #include <stdlib.h>
    #include "Haffman.h"
    #define MaxValue 10000
    using namespace std;
    
    int main(){
    	cout<<"--------------Huffman文件压缩编码---------------"<<endl;
    	cout<<"请输入文件名:";
    	char filename[256];//文件名
    	cin>>filename;
    
    	char ch;
    	int i;
    	int n = 256;
    	int weight[256]={0};
    
    	//以二进制流的方式打开文件
    	FILE* in = fopen(filename,"rb");
    	if(in == NULL)
    	{
    		printf("打开文件失败");
    		return 0;
    	}
    	//扫描文件,获得权重
    	while(ch = getc(in) != EOF)
    	{
    		weight[ch]++;
    	}
    	//关闭文件
    	fclose(in);
    
    	//测试哈夫曼树构造函数
    	HaffNode *myHaffNode = (HaffNode *)malloc(sizeof(HaffNode)*(2*n-1));
    	if(n >MaxValue)
    	{
    		printf("给出的n越界,修改MaxValue");
    		exit(1);
    	}
    	Haffman(weight,n,myHaffNode);
    
    	printf("字节种类 权值 标记 双亲结点下标 左孩子结点下标 右孩子结点下标\n");
    	for(i = 0;i < n;i++)
    	{
    		printf("pHT[%d]\t%d\t%d\t%d\t%d\t%d\n",i,myHaffNode[i].weight,myHaffNode[i].flag,myHaffNode[i].parent,myHaffNode[i].leftChild,myHaffNode[i].rightChild);
    	}
    	system("pause");
    	return 0;
    }

    运行结果如下:

    (5)生成哈夫曼编码

    按照先序遍历的算法对上面生成的哈夫曼树进行遍历,生成哈夫曼编码。

    在Haffman.h文件中创建结构体哈夫曼数组,用于存储编码的起始下表和权值。

    在Haffman.cpp文件中创建构造哈夫曼树编码的函数,在Haffman.h文件中声明。

    Haffman.h文件

    typedef struct
    {
    	int weight;		//权值
    	int flag;	    //标记
    	int parent;		//双亲结点下标
    	int leftChild;	//左孩子下标
    	int rightChild;	//右孩子下标
    }HaffNode;
    
    typedef struct
    {
    	int bit[10000];	//数组
    	int start;	//编码的起始下标
    	int weight;	//字符的权值
    }Code;
    
    //创建叶结点个数为n,权值数组为weight的哈夫曼树haffTree
    void Haffman(int weight[],int n,HaffNode haffTree[]);
    
    //有n个结点的哈夫曼树haffTree构造哈夫曼编码haffCode
    void HaffmanCode(HaffNode haffTree[],int n,Code haffNode[]);

    Haffman.cpp文件

    //有n个结点的哈夫曼树haffTree构造哈夫曼编码haffCode
    void HaffmanCode(HaffNode haffTree[],int n,Code haffCode[])
    {
    	Code *cd = (Code *)malloc(sizeof(Code));
    	int i,j,child,parent;
    	//求n个叶结点的哈夫曼编码
    	for(int i = 0;i < n;i++)
    	{
    		cd->start  = n-1;					//不等长编码的最后一位为n-1
    		cd->weight = haffTree[i].weight;	//取得编码对应的权值
    		child  = i;
    		parent = haffTree[child].weight;
    		//由叶结点向上直到根节点
    		while(parent != -1)
    		{
    			if(haffTree[parent].leftChild == child)	//判断左孩子是否存在
    				cd->bit[cd->start] = 0;
    			else									//判断右孩子是否存在
    				cd->bit[cd->start] = 1;
    			cd->start--;
    			child  = parent;
    			parent = haffTree[child].parent;
    		}
    		for(j = cd->start+1;j < n;j++)
    			haffCode[j].bit[j] = cd->bit[j];
    		haffCode[i].start  = cd->start + 1;
    		haffCode[i].weight = cd->weight;
    	}
    }

    现在在主函数中测试一下:

    #include <iostream>
    #include <stdlib.h>
    #include "Haffman.h"
    using namespace std;
    #define MaxValue 10000
    
    
    int main(){
    	cout<<"--------------Huffman文件压缩编码---------------"<<endl;
    	cout<<"请输入文件名:";
    	char filename[256];//文件名
    	cin>>filename;
    
    	char ch;
    	int i,j;
    	int n = 256;
    	int weight[256]={0};
    
    	//以二进制流的方式打开文件
    	FILE* in = fopen(filename,"rb");
    	if(in == NULL)
    	{
    		printf("打开文件失败");
    		fclose(in);
    		return 0;
    	}
    	//扫描文件,获得权重
    	while(ch = fgetc(in) != EOF)
    	{
    		weight[ch]++;
    	}
    	
    	//关闭文件
    	fclose(in);
    
    	
    	//显示256个字节出现的次数
    	cout<<"Byte "<<"Weight"<<endl;
    	for(i=0;i<256;i++)
    	{
    		printf("0x%02X %d\n",i,weight[i]);
    	}
    	
    	HaffNode *myHaffNode = (HaffNode *)malloc(sizeof(HaffNode)*(2*n-1));
    	Code *myHaffCode = (Code *)malloc(sizeof(Code)*n);
    	if(n >MaxValue)
    	{
    		printf("给出的n越界,修改MaxValue");
    		exit(1);
    	}
    	
    
    	//测试哈夫曼树构造函数
    	Haffman(weight,n,myHaffNode);
    	//测试哈夫曼编码函数
    	HaffmanCode(myHaffNode,n,myHaffCode);
    
    	//输出每个字节叶结点的哈夫曼编码
    	printf("编码信息为:");
    	for(i = 0;i < n;i++)
    	{
    		//printf("0x%02X ",i);
    		printf("Weight = %d,Code = ",myHaffCode[i].weight);
    		for(j = myHaffCode[i].start;j < n;j++)
    			printf("%d",myHaffCode[i].bit[j]);
    		printf("\n");
    	} 
    
    	system("pause");
    	return 0;
    }

    运行结果如下:

    这里我们发现权值为141291的字节没有显示编码,原因是这个编码个数太长了,在这里显示不出来。

    (6)压缩源文件

    创建Compress.h和Compress,cpp文件,定义Compress函数用于压缩原文件。

    由于编码是以字符数组的形式保存的,重新编码后的数据将是一个很长的字符串,先计算需要的空间,然后把编码按位进行存放到字符数组中,或者直接存放,这里采用直接存放的方式。

    int k,ji=0;
    	int jiShu[1000];
    	//输出每个字节叶结点的哈夫曼编码
    	printf("编码信息为:");
    	for(i = 0;i < n;i++)
    	{
    		for(j = myHaffCode[i].start;j < n;j++)
    			for(k = 0;k < myHaffCode[k].weight+1;k++)
    			{
    				printf("%d",myHaffCode[i].bit[j]);
    				jiShu[ji] = myHaffCode[i].bit[j];
    				ji++;
    			}
    	} 

    结果如下:

    (6)写入文件

    建立一个新文件,文件名为"原文件名字+.huf",将压缩后的数据写入文件。

    为了保证压缩后的数据能够被正确解压,必须把相关的解压缩规则写进去,就是把权值信息写入进去,还有文件类型,长度,权值。

    在Huffman.h中定义一个文件头结构和InitHead函数声明,在Huffman.cpp中写入函数。

    代码如下:
    Huffman.h文件

    struct HEAD
    {
    	char type[4]; //文件类型
    	int length;	  //原文件长度
    	int weight[256]; //权值数组
    }

    Huffman.cpp文件

    //记录文件信息
    int initHead(char pFileName[256],HEAD &sHead)
    {
    	int i;
    	//初始化文件头
    	strcpy(sHead.type,"bmp");
    	sHead.length = 0;	//原文件长度
    	for(i = 0;i<256;i++)
    	{
    		sHead.weight[i] = 0;
    	}
    
    	//以二进制流的方式打开文件
    	FILE* in = fopen(pFileName,"rb");
    	if(in == NULL)
    	{
    		printf("打开文件失败");
    		fclose(in);
    		return 0;
    	}
    	char ch;
    	//扫描文件,获得权重
    	while(ch = fgetc(in) != EOF)
    	{
    		sHead.weight[ch]++;
    		sHead.length++;
    	}
            //关闭文件
    	fclose(in);
    	in = NULL;
    	return 0;
    }

    现在我们得到文件的信息和编码,就可以得到压缩后的文件,直接在主函数中写代码:

    #include <iostream>
    #include <stdlib.h>
    #include <io.h>
    #include "Haffman.h"
    //#include "Compress.h"
    using namespace std;
    #define MaxValue 10000
    
    
    int main(){
    	cout<<"--------------Huffman文件压缩编码---------------"<<endl;
    	cout<<"请输入文件名:";
    	char filename[256];//文件名
    	cin>>filename;
    
    	char ch;
    	int i,j;
    	int n = 256;
    	int weight[256]={0};
    
    	//以二进制流的方式打开文件
    	FILE* in = fopen(filename,"rb");
    	int fn = _fileno(in); /*取得文件指针的底层流式文件号*/
    	int sz = _filelength(fn);/*根据文件号取得文件大小*/
    	printf("%d字节\n",sz);
    	if(in == NULL)
    	{
    		printf("打开文件失败");
    		fclose(in);
    		return 0;
    	}
    	//扫描文件,获得权重
    	while(ch = fgetc(in) != EOF)
    	{
    		weight[ch]++;
    	}
    	
    	//关闭文件
    	fclose(in);
    
    	/*
    	//显示256个字节出现的次数
    	cout<<"Byte "<<"Weight"<<endl;
    	for(i=0;i<256;i++)
    	{
    		printf("0x%02X %d\n",i,weight[i]);
    	}
    	*/
    	
    	HaffNode *myHaffNode = (HaffNode *)malloc(sizeof(HaffNode)*(2*n-1));
    	Code *myHaffCode = (Code *)malloc(sizeof(Code)*n);
    	if(n >MaxValue)
    	{
    		printf("给出的n越界,修改MaxValue");
    		exit(1);
    	}
    	
    
    	//测试哈夫曼树构造函数
    	Haffman(weight,n,myHaffNode);
    	//测试哈夫曼编码函数
    	HaffmanCode(myHaffNode,n,myHaffCode);
    
    	/*
    	printf("字节种类 权值 标记 双亲结点下标 左孩子结点下标 右孩子结点下标\n");
    	for(i = 0;i < n;i++)
    	{
    		printf("pHT[%d]\t%d\t%d\t%d\t%d\t%d\n",i,myHaffNode[i].weight,myHaffNode[i].flag,myHaffNode[i].parent,myHaffNode[i].leftChild,myHaffNode[i].rightChild);
    	}
    	*/
    	
    	HEAD sHead;
    	initHead(filename,sHead);
    
    	int cc=0;
    	//生成文件名
    	char newFileName[256] = {0};
    	strcpy(newFileName,filename);
    	strcat(newFileName,".huf");
    	//以二进制流的方式打开文件
    	FILE* out = fopen(newFileName,"wb");
    	//写文件头
    	//fwrite(&sHead,sizeof(HEAD),1,out);
    	//int k,ji=0;
    	//int jiShu[1000];
    	//输出每个字节叶结点的哈夫曼编码
    	//printf("编码信息为:");
    	for(i = 0;i < n;i++)
    	{
    		for(j = myHaffCode[i].start;j < n;j++)
    		{
    				//printf("%d",myHaffCode[i].bit[j]);
    				//写压缩后的编码
    				cc+=sizeof(myHaffCode[i].bit[j]);
    		}
    
    	} 
    
    	fclose(out);
    	out = NULL;
    	cout << "生成压缩文件:" << newFileName <<endl;
    	printf("\n");
    	printf("%d字节",sizeof(newFileName)+cc);
    	double bi=(sizeof(newFileName)+cc)/(double)sz;
    	printf("压缩比例为:%.2f",bi);
    	system("pause");
    	return 0;
    }

    结果如下:

    而我们也生成了该buf的压缩文件:

     

    需要详细代码请私信我:

    qq:1657264184

    微信:liang71471494741

     

     

    展开全文
  • 好久没更新博客,每一次的跟新也是对自己所学的知识一次总结所以今次依旧保持原来的风格,白话化,今天要讲的是文件压缩和解压缩学过数据结构的读者应该知道,是用哈夫曼的方法去,但是往往我们学了个大概就没学...


    好久没更新博客,每一次的跟新也是对自己所学的知识一次总结
    所以今次依旧保持原来的风格,白话化,今天要讲的是文件的压缩和解压缩
    学过数据结构的读者应该知道,是用哈夫曼的方法去做,但是往往我们学了个
    大概就没学下去,很快就忘了,所以今天想这个知识总结下,做一个完整的例子
    好吧,那就开始吧
    首先要大家明白一个概念,为什么文件能够压缩,就好像有mp3啊,视频啊,图片啊,这几种我没研究
    过,但如果要到现实上说的话我们通常把多余的空隙去掉,比如你去送一车货,你肯定想装多一点货
    但如果货物时不是正方形的,如果是三角形的形状的话或者其他形状的话那就很难压缩了,在货物不
    变形的情况下,所以很多时候是尽量把空出来的的一些位置放上其他小东西,这样就不会太浪费啦
    好了,回到我们今天所说的话题,文件文本压缩,一般来说大家应该知道,一个字符时用8个字节表示的
    那么针对一些出现的字符00000010,前面这么多的0就是浪费,也不能说浪费,怎么说呢,都是举个例子吧
    比如一个文本的内容
    AAGGDCCCC
    就这么几个字符,有人立即就会想到,就这么几个字符,不用8位去表示一个字符吧,其实是对的,就只有
    4种类型,用两位表示就可以啦, A 为00,G为01,d为10,c为11, 就这样,不就可以吗
    对,是可以的,压缩后 的文件可以这样表示(空格忽略)
    00 00 01 01 10 11 11 11 11
    A A G G D C C C C
    总共才2个字节多一点(22bits),原来的大小是10个字节,压缩率也不错,是吧
    但别开心的这么早,那么我们收到这个压缩过的文件怎么解压缩呢,因为你要把 00 还原成
    A的asscii码才能显示啊,所以就这样的话事不行的,需要在压缩的文件追加一些信息提供解压缩用的
    那我们定义一个结构吧
    struct node
    {
    char b //表示代表的字符
    char bits[2] //表示编码后的位数
    };
    那么追加后的信息为 0x41 0x00 0x00 0x47 0x00 0x01 .........
    其他就不写啦
    如果这样我们看看能不能解压缩,
    我们先初始化结构
    fread(&node[i],sizeof(struct node)1,fp);
    其实这里必须要知道从哪里开始读取结构数据,要知道这个,必须知道压缩后的长度是多小,再移动文件指针去读取
    好了,首先我们预先知道每个字符时用2个字符编码的,每当我们读取一个字节出来的时候,我们就要对它解码
    再写入新的文件
    比如 00 00 01 01 我们读出的第一个字节是这样的
    先 itoa(f,buf,2); 把二进制转换成字符就是"00000101"这样,注意二进制00 00 01 01和"00000101"
    是不同的,前者占一个字节,后者占8个字节,不懂的去看下asccii表字符串的定义
    再循环我们的结构通过 比较
    memcmp(node[i].bits,buf,2)==0 就知道所代表的对的字符,然后写进文件就可以啦
    比如第一个00 对应结构的bits 的A,然后把A写进去就可以啦
    到了这里,我们是否考虑下呢 压缩率
    我们为了可以解压缩,加多了12个字节 (4*3),压缩率大大降下,我们是否应该想下其他办法呢,从另外一个角度看
    这样的压缩式没有什么意思的,比如文件 一旦字符的种类个数每个字符都出现过,那还是要8位编码去表示
    每一个字符
    例如文件的内容是 aa bb cc dd..................zz,还有一些不可打印的字符,例如回车换行符啊,所以要想做到
    以不变应万变的话,上面的程序是不可行的,只因它还是定长编码,那有没有其他方法啦,是有的那就是变长
    编码,主角哈夫曼编码要出现啦,这个东西在上面的基础作了进一步的改进,就是他是根据字符的出现频率
    来确定该字符的编码长度,出现越高频率的字符编码越短,这样才能减小文件大小啊,大家都懂吧
    这样的话还有一个条件,就是两两编码字符间不可以一个是另一个的前序,不然会解码错误
    memcmp(node[i].bits,buf,2)==0,大家可以根据这个来参悟,哈夫曼树两个就符合了,其实他也是二叉树的一种
    下面把他的结构列出来
    struct head
    {
    unsigned char b; //记录字符在数组中的位置
    long count; //字符出现频率(权值)
    long parent,lch,rch; //定义哈夫曼树指针变量 父亲,左儿子,右儿子 指针
    char bits[256]; //定义存储哈夫曼编码的数组
    }
    这里为什么不用指针呢,是为了方便我们的程序,就那我们的上面的例子做例子
    A A G G D C C C C
    A(2) C(4) D(1) G(2)
    那么哈夫慢树就是这样
    (9)
    (5) C(4 编码 0)


    G(2 编码 11) (3)

    D(1 编码 101) A(2 编码 100)

    好了,我们就计算一下压缩率 A = 3*2 = 6; D = 3*1 = 3 G = 2*2 =4 C = 1*4 =4
    17bits ,也就是3个字节,这根本没什么,最后那1bit 要补0,比上面还小啊,所以这算法就是牛,好了
    所有知识都差不多讲完啦,开始我们的程序之旅吧,解压缩思想跟上面的差不多有一点不同的是
    注意(哈夫曼编码不是字符串存入的,是合成存储的,不明的留意给我)
    压缩:
    1,定义哈夫曼树结构和生成哈夫曼程序,
    2,哈夫曼编码,
    3,循环把源文件进行哈夫曼编码存进新的文件
    4,吧哈夫曼结构有用的信息追加文件后面,解码用的

    解压缩的
    1读入文件压缩文件长度,移动文件指针
    2,还原哈夫曼结构
    3,移动文件指针,依次读入压缩字节进行解码,比较哈夫曼编码
    4,吧字节写进新文件里,
    5解压缩成功

     

     

     

    #include <stdio.h> 
    #include <string.h> 
    #include <stdlib.h> 
    #include <conio.h>
    struct head 
    {
    unsigned char b;        //记录字符在数组中的位置
        long count;             //字符出现频率(权值) 
        long parent,lch,rch;    //定义哈夫曼树指针变量
        char bits[256];         //定义存储哈夫曼编码的数组
    } 
    header[512],tmp;
    /*压缩*/
    void compress() 
    {
    char filename[255],outputfile[255],buf[512]; 
        unsigned char c; 
        long i,j,m,n,f; 
        long min1,pt1,flength,length1,length2; 
    double div;
        FILE *ifp,*ofp; 
        printf("\t请您输入需要压缩的文件:"); 
        gets(filename); 
        ifp=fopen(filename,"rb"); 
        if(ifp==NULL) 
    {
       printf("\n\t文件打开失败!\n\n"); 
            return; 
    }
    printf("\t请您输入压缩后的文件名:"); 
        gets(outputfile); 
        ofp=fopen(strcat(outputfile,".hub"),"wb"); 
        if(ofp==NULL) 
    {
       printf("\n\t压缩文件失败!\n\n"); 
            return; 
    }
    flength=0; 
        while(!feof(ifp)) 
    {
       fread(&c,1,1,ifp); 
            header[c].count++;    //字符重复出现频率+1
            flength++;            //字符出现原文件长度+1
    }
    flength--; 
        length1=flength;          //原文件长度用作求压缩率的分母
        header[c].count--; 
        for(i=0;i<512;i++) 
    {
       if(header[i].count!=0) header[i].b=(unsigned char)i; 
       /*将每个哈夫曼码值及其对应的ASCII码存放在一维数组header[i]中,
              且编码表中的下标和ASCII码满足顺序存放关系*/
            else header[i].b=0; 
            header[i].parent=-1;header[i].lch=header[i].rch=-1;    //对结点进行初始化
    } 
        for(i=0;i<256;i++)    //根据频率(权值)大小,对结点进行排序,选择较小的结点进树
    {
       for(j=i+1;j<256;j++)      //这里运用冒泡排序法啊
       {
        if(header[i].count<header[j].count)
        {
         tmp=header[i];
                    header[i]=header[j]; 
                    header[j]=tmp; 
        } 
       } 
    }
    for(i=0;i<256;i++)
        if(header[i].count==0) break; 
      
        
        n=i;       //外部叶子结点数为n个时,内部结点数为n-1,整个哈夫曼树的需要的结点数为2*n-1.
        m=2*n-1;
        for(i=n;i<m;i++)   //构建哈夫曼树
    {
       min1=999999999;   //预设的最大权值,即结点出现的最大次数
            for(j=0;j<i;j++) 
       {
        if(header[j].parent!=-1) continue;    
        //parent!=-1说明该结点已存在哈夫曼树中,跳出循环重新选择新结点*/
                if(min1>header[j].count) 
        {
                   pt1=j; 
                    min1=header[j].count; 
                    continue; 
        } 
       }
       //上面已经取出最小的
       header[i].count=header[pt1].count; 
            header[pt1].parent=i;   //依据parent域值(结点层数)确定树中结点之间的关系
            header[i].lch=pt1;   //计算左分支权值大小
            min1=999999999;   
            for(j=0;j<i;j++)            //这里运用选择排序法啊
       {
        if(header[j].parent!=-1) continue; 
                if(min1>header[j].count) 
        {
         pt1=j; 
                    min1=header[j].count; 
                    continue; 
        } 
       }
       header[i].count+=header[pt1].count; 
            header[i].rch=pt1;   //计算右分支权值大小
            header[pt1].parent=i; 
    }
    for(i=0;i<n;i++)   //哈夫曼无重复前缀编码
    {
       f=i; 
            header[i].bits[0]=0;   //根结点编码0   
            while(header[f].parent!=-1) 
       {
        j=f;  //也就是i
                f=header[f].parent;  //d 12
                if(header[f].lch==j)   //置左分支编码0
        {
         j=strlen(header[i].bits); 
                    memmove(header[i].bits+1,header[i].bits,j+1);
         //依次存储连接“0”“1”编码
                    header[i].bits[0]='0'; 
        }
        else   //置右分支编码1
        {
         j=strlen(header[i].bits); 
                    memmove(header[i].bits+1,header[i].bits,j+1); 
                    header[i].bits[0]='1'; 
        } 
       } 
    }
    fseek(ifp,0,SEEK_SET);   //从文件开始位置向前移动0字节,即定位到文件开始位置
        fwrite(&flength,sizeof(int),1,ofp);
    /*用来将数据写入文件流中,参数flength指向欲写入的数据地址,
    总共写入的字符数以参数size*int来决定,返回实际写入的int数目1*/ 
        fseek(ofp,8,SEEK_SET); 
        buf[0]=0;   //定义缓冲区,它的二进制表示00000000
        f=0; 
        pt1=8; 
    /*假设原文件第一个字符是"A",8位2进制为01000001,编码后为0110识别编码第一个'0',
    那么我们就可以将其左移一位,看起来没什么变化。下一个是'1',应该|1,结果00000001 
    同理4位都做完,应该是00000110,由于字节中的8位并没有全部用完,我们应该继续读下一个字符,
    根据编码表继续拼完剩下的4位,如果字符的编码不足4位,还要继续读一个字符,
    如果字符编码超过4位,那么我们将把剩下的位信息拼接到一个新的字节里*/
        while(!feof(ifp)) 
    {
       c=fgetc(ifp); 
            f++; 
            for(i=0;i<n;i++) 
       {
        if(c==header[i].b) break; 
       }
       strcat(buf,header[i].bits); 
            j=strlen(buf);
            c=0; 
       while(j>=8)   //对哈夫曼编码位操作进行压缩存储 //23.255814
       {
        for(i=0;i<8;i++) 
        {
         if(buf[i]=='1') c=(c<<1)|1; 
                    else c=c<<1; 
        }
        fwrite(&c,1,1,ofp); 
                pt1++;   //统计压缩后文件的长度
                strcpy(buf,buf+8);   //一个字节一个字节拼接
                j=strlen(buf); 
       }
       if(f==flength) break; 
    }
        if(j>0)    //对哈夫曼编码位操作进行压缩存储
        {
            strcat(buf,"00000000"); 
            for(i=0;i<8;i++) 
       {
        if(buf[i]=='1') c=(c<<1)|1; 
                else c=c<<1; 
       }
       fwrite(&c,1,1,ofp); 
            pt1++; 
    }
    fseek(ofp,4,SEEK_SET); //移动文件指针位置到第四位
        fwrite(&pt1,sizeof(long),1,ofp);  //写入统计压缩后文件的长度
        fseek(ofp,pt1,SEEK_SET); //移动文件指针到压缩后文件的长度后的位置
        fwrite(&n,sizeof(long),1,ofp); //写入节点数目 为13
        for(i=0;i<n;i++) 
    {
       fwrite(&(header[i].b),1,1,ofp);  //写入每个节点的代表的字符
            c=strlen(header[i].bits); 
            fwrite(&c,1,1,ofp); //写入每个字符哈夫曼编码的长度
            j=strlen(header[i].bits); //统计哈夫曼长度
            if(j%8!=0)   //若存储的位数不是8的倍数,则补0   
       {
        for(f=j%8;f<8;f++)  //比如长度只为3             //比如 9
                strcat(header[i].bits,"0"); //011 00000      01010101 10000000
       }
       while(header[i].bits[0]!=0)  //这里检查是否到了字符串末尾
       {
        c=0; 
                for(j=0;j<8;j++)   //字符的有效存储不超过8位,则对有效位数左移实现两字符编码的连接
        {
         if(header[i].bits[j]=='1')
             c=(c<<1)|1;   //|1不改变原位置上的“0”“1”值
                    else c=c<<1; 
        }
        strcpy(header[i].bits,header[i].bits+8);   //把字符的编码按原先存储顺序连接
                fwrite(&c,1,1,ofp); 
       } 
    } 
    length2=pt1--;
    div=((double)length1-(double)length2)/(double)length1;   //计算文件的压缩率
        fclose(ifp); 
        fclose(ofp); 
        printf("\n\t压缩文件成功!\n"); 
        printf("\t压缩率为 %f%%\n\n",div*100); 
        return; 
    }
    /*解压缩*/
    void uncompress() 
    {
    char filename[255],outputfile[255],buf[255],bx[255]; 
        unsigned char c; 
        long i,j,m,n,f,p,l; 
        long flength; 
        FILE *ifp,*ofp; 
        printf("\t请您输入需要解压缩的文件:"); 
        gets(filename); 
        ifp=fopen(strcat(filename,".hub"),"rb"); 
        if(ifp==NULL) 
    {
       printf("\n\t文件打开失败!\n"); 
            return; 
    }
    printf("\t请您输入解压缩后的文件名:"); 
        gets(outputfile); 
        ofp=fopen(outputfile,"wb"); 
        if(ofp==NULL) 
    {
       printf("\n\t解压缩文件失败!\n"); 
            return; 
    }
    fread(&flength,sizeof(long),1,ifp);   //读取原文件长度,对文件进行定位
        fread(&f,sizeof(long),1,ifp); //压缩长度
        fseek(ifp,f,SEEK_SET); //长度后的节点数目
        fread(&n,sizeof(long),1,ifp);  //节点数
        for(i=0;i<n;i++) 
    {
       fread(&header[i].b,1,1,ifp); //字符值 
            fread(&c,1,1,ifp);  
            p=(long)c;   //读取原文件字符的权值
            header[i].count=p; //哈夫曼的编码长度
            header[i].bits[0]=0; 
            if(p%8>0) m=p/8+1; //字节数
            else m=p/8;  //m=1
            for(j=0;j<m;j++) 
       {
        fread(&c,1,1,ifp); 
                f=c; 
                itoa(f,buf,2);   //将f转换为二进制表示的字符串
                f=strlen(buf); 
                for(l=8;l>f;l--) 
        {
         strcat(header[i].bits,"0"); 
        }
        strcat(header[i].bits,buf); 
       } 
            header[i].bits[p]=0; 
    } 
        for(i=0;i<n;i++)   //根据哈夫曼编码的长短,对结点进行排序(从小到大啊)
    {
       for(j=i+1;j<n;j++)   //冒泡排序
       {
        if(strlen(header[i].bits)>strlen(header[j].bits)) 
        {
         tmp=header[i]; 
                    header[i]=header[j]; 
                    header[j]=tmp; 
        } 
       } 
    } 
        p=strlen(header[n-1].bits);  //最大长度
        fseek(ifp,8,SEEK_SET);  //移动文件指针
        m=0; 
        bx[0]=0; 
        while(1)    //通过哈夫曼编码的长短,依次解码,从原来的位存储还原到字节存储
    {
       while(strlen(bx)<(unsigned int)p) 
       {
        fread(&c,1,1,ifp); 
                f=c; 
                itoa(f,buf,2); 
                f=strlen(buf); 
                for(l=8;l>f;l--) //在单字节内对相应位置补0
        {
         strcat(bx,"0"); 
        }
        strcat(bx,buf); 
       }
       for(i=0;i<n;i++) 
       {
        if(memcmp(header[i].bits,bx,header[i].count)==0) break; 
       }
       strcpy(bx,bx+header[i].count);   /*从压缩文件中的按位存储还原到按字节存储字符,
                                         字符位置不改变*/
       c=header[i].b; 
            fwrite(&c,1,1,ofp); 
            m++;   //统计解压缩后文件的长度
            if(m==flength) break;   //flength是原文件长度
    } 
        fclose(ifp); 
        fclose(ofp); 
        printf("\n\t解压缩文件成功!\n"); 
        if(m==flength)   //对解压缩后文件和原文件相同性比较进行判断(根据文件大小)
    printf("\t解压缩文件与原文件相同!\n\n"); 
    else printf("\t解压缩文件与原文件不同!\n\n");
        return; 
    }
    /*主函数*/
    int main() 
    {
    int c; 
        while(1)   //菜单工具栏
    {
       printf("\t _______________________________________________\n");
            printf("\n");
            printf("\t             * 压缩、解压缩 小工具 *            \n");
            printf("\t _______________________________________________\n");    
    
            printf("\t _______________________________________________\n");
            printf("\t|                                               |\n");   
            printf("\t| 1.压缩                                       |\n");   
            printf("\t| 2.解压缩                                     |\n");   
            printf("\t| 0.退出                                       |\n");
            printf("\t|_______________________________________________|\n");
       printf("\n");
       printf("\t                 说明:(1)采用哈夫曼编码\n");
         printf("\t                       (2)适用于字符型文本文件\n"); 
       printf("\n");
         do   //对用户输入进行容错处理
       {
        printf("\n\t*请选择相应功能(0-2):");     
                c=getch(); 
        printf("%c\n",c); 
                if(c!='0' && c!='1' && c!='2')
        { 
         printf("\t@_@请检查您输入的数字在0~2之间!\n");
                    printf("\t请再输入一遍!\n");
        }
       }while(c!='0' && c!='1' && c!='2'); 
       if(c=='1') compress();          //调用压缩子函数
            else if(c=='2') uncompress();   //调用解压缩子函数
       else 
       {
        printf("\t欢迎您再次使用该工具^_^\n"); 
          exit(0);                    //退出该工具
       }
       system("pause");   //任意键继续
            system("cls");     //清屏
    }
        return 0;
    }

    转载于:https://www.cnblogs.com/simonlu/archive/2012/10/21/2733106.html

    展开全文
  • 在FTP的数据传输中 传输模式将决定文件数据会以什么方式被发送出去 一般情况下 网络传输模式有3种 将数据格式化后传送 ...这种传输模式下没有数据结构类型的限制 2 文件模式 文件模式就是以文件结构的...

    在FTP的数据传输中
    传输模式将决定文件数据会以什么方式被发送出去

    一般情况下
    网络传输模式有3种
    将数据格式化后传送
    压缩后传送
    不做任何处理进行传送

    当然不论用什么模式进行传送
    在数据的结尾处都是以EOF结束

    定义的传输模式有以下三种
    1 二进制模式
    二进制模式就是将发送数据的内容转换为二进制表示后再进行传送
    这种传输模式下没有数据结构类型的限制

    2 文件模式
    文件模式就是以文件结构的形式进行数据传输
    文件结构是指用一些特定标记来描述文件的属性以及内容

    3 压缩模式
    在这种模式 需要传送的信息包括一般数据 
    压缩数据和控制指令

    一般数据 以字节的形式进行传送
    压缩数据 包括数据副本和数据过滤器
    控制命令 用两个转义字符进行传送

     

    在FTP数据传输时
    发送方必须把数据转换成文件结构指定的形式再传送出去
    而接收方相反
    因为进行这样的转换很慢
    所以一般在相同的系统中传送文本文件时都采用二进制流表示比较合适

    展开全文
  • .pak文件

    千次阅读 2011-05-12 21:00:00
    j2me 不理解为何将许多相关的图片整合成一张,然后再程序中进行图片的裁剪,为什么了啊?   <br />2. 由于MIDP对j2me程序大小有限制,所以缩小发布程序就意味着能够提供更多的程序或者内容(如图片、...

    1. 做j2me 不理解为何将许多相关的图片整合成一张,然后再程序中进行图片的裁剪,为什么了啊?

     

    2. 由于MIDPj2me程序大小有限制,所以缩小发布程序就意味着能够提供更多的程序或者内容(如图片、音乐)给用户。

        zip/jar算法对大文件的压缩率高于对等量的多个小文件的压缩率

     

    3.先把多个小文件按一定结构组合成一个pak文件,再压缩成jar包,有助于减少程序容量。同时可以在pak文件中对文件数据进行加密,可以保护作者权益。

     


    几点好处: 
        1.对于程开发人员来说资源调用方便,易於管理;   
         2.由于结构特殊,对初学者有屏蔽作用避免被乱改,对高手却很容易修改,比较方便;   
         3.所有模式都是利用这个特点做的;   
         4pak包被游戏的引擎视为一个目录,能利用外置pak文件的形式进行升级而无需删除原有文件。利於增加效果包,新地图,改进界面,增         加model等扩展内容;   
         5.修改还原便利,文件之间互不影响。   
            如果想要打开pak结尾的文件,使用PAk浏览器   
         RPGViewer 3.0 
       下载了以后一定要看说明书(readme.txt)   
         可以直接查看PAK格式的内容   
         PAK解压:   
         工具里面有解压包的按钮,可以解压PAK


    展开全文
  • 1.24 我在一个文件中定义了一个extern数组,然后在另一个文件中使用,为什么sizeof取不到数组的大小? 42 声明问题 43 1.25 函数只定义了一次,调用了一次,但编译器提示非法重声明了。 43 *1.26 main的正确定义...
  • 游戏编程--数据管理

    2008-10-08 10:56:11
     那么为什么要使用这种压缩包呢?原因很多,首先就是压缩,游戏中使用的图片等资源都是极其适合压缩的,这样就减小了游戏的体积。如果把《红色警戒2》的资源全部解开相信会超过3G。其次压缩文件体积的减小会带来...
  •  本书是一本关于oracle database 9i、10g 和11g 数据库体系结构的权威图书,涵盖了所有重要的oracle 体系结构特性,包括文件、内存结构和进程,锁和闩,事务、并发和多版本,表和索引,数据类型,分区和并行,以及...
  • 7.4.3 为什么重启动对我们很重要? 250 7.5 小结 251 第8章 事务 253 8.1 事务控制语句 254 8.2 原子性 255 8.2.1 语句级原子性 255 8.2.2 过程级原子性 257 8.2.3 事务级原子性 260 8.3 完整性约束和事务 ...
  •  本书是一本关于Oracle 9i & 10g数据库体系结构的权威图书,涵盖了所有最重要的Oracle体系结构特性,包括文件、内存结构和进程,锁和闩,事务、并发和多版本,表和索引,数据类型,以及分区和并行,并利用具体的...
  • 105页的数据结构和算法 142页的Spring 58页的过滤器和监听器 30页的HTTP xxxx页的Redis:待更新...(最新首发公众号,导航更新可能有延迟) Hibernate AJAX ...... :coffee:Java基础 初学者学Java常遇到的问题,我...
  • 利用的是Zip文件“可以添加comment(摘要)”的数据结构特点,在文件的末尾写入任意数据,而不用重新解压zip文件(apk文件就是zip文件格式); 所以该工具不需要对apk文件压缩和重新签名即可完成多渠道自动打包,...
  • 此程序是学校数据结构课程设计而的。 运行方法: 可通过打开校园导游系统.exe或校园导游系统.hta(不明白hta是什么的可以自行百度,虽然本人也是第一次使用)进入登陆界面。之后登陆界面会调用相应exe文件...
  • Axx:ARJ压缩文件的分包序号文件,用于将一个大文件压至几个小的压缩包中(xx取01-99的数字) A3L:Authorware 3.x库文件 A4L:Authorware 4.x库文件 A5L:Authorware 5.x库文件 A3M,A4M:Authorware Macintosh...
  • Hadoop实战(第2版)

    2015-10-26 14:28:46
    7 数据结构和算法的运用 7.1 使用图进行数据建模和解决问题 7.1.1 模拟图 7.1.2 最短路径算法 技术点52 找出两个用户间的最短距离 7.1.3 friends-of-friends(FoF) 技术点53 计算FoF 7.1.4 ...
  • Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输 Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据文件、...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
  • 直筒子等这种为什么不直接把下级往上提一级来区级,采用补齐填充的方式来对齐数据的原因,请参考issue#9。 【字段】ok_data表 省市区镇数据表。 字段 类型 描述 id int/long 城市编号,三级用int类型,四...
  • 14.1 Windows为什么会崩溃 845 14.2 蓝屏 846 14.3 崩溃转储文件 849 崩溃转储的生成 852 14.4 Windows错误报告 853 14.5 在线崩溃分析 854 14.6 基本的崩溃转储分析 855 Notmyfault 855 基本的崩溃转储分析 856 ...
  • 14.1 Windows为什么会崩溃 845 14.2 蓝屏 846 14.3 崩溃转储文件 849 崩溃转储的生成 852 14.4 Windows错误报告 853 14.5 在线崩溃分析 854 14.6 基本的崩溃转储分析 855 Notmyfault 855 基本的崩溃转储分析 856 ...
  • MAPGIS地质制图工具

    2013-05-06 16:15:30
    1、MapGis数据转入Excel后表格内容空白什么原因? 答:可能原因是转的时候没有正常关闭com接口,可以关闭Section,重新打开Section再试试,或者打开任务管理器,关闭所有Excel进程。 2、Excel单元格数据转入MapGis...
  • [Oracle.11g权威指南(第2版)].谷长勇.扫描版.pdf

    千次下载 热门讨论 2013-06-23 21:16:09
    第四部分Oracle 11g数据库管理,主要介绍管理控制文件/重做日志文件/表空间/数据文件、模式对象管理、表分区和索引分区、用户管理与安全、数据完整性和数据约束等;第五部分Oracle数据库优化,着重介绍Oracle...
  • 2.5.4 半结构数据模型 30 第3章 Oracle DBA的UNIX/Linux基础 31 3.1 UNIX和Linux操作系统概述 31 3.1.1 UNIX 31 3.1.2 Linux 32 3.1.3 中档系统 32 3.2 理解UNIX shell 33 3.3 访问UNIX系统 33 3.4...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 258
精华内容 103
关键字:

数据结构为什么做文件压缩

数据结构 订阅