数据压缩的重要组成部分---位操做

数据压缩描述

数据压缩是一个减小数据存储空间的过程。数据结构

数据压缩包括两个过程:一个过程是,压缩或编码数据,数据大小减少;另外一个过程是,解压缩或解码数据,还原到数据自己的状态。函数

根据信息的内容,全部的数据都会表现出必定的特性,称为熵(从热力学借用的一个术语)。压缩是可能的,由于绝大多数数据所表现出来的容量都大于其熵所建议的最佳容量。为了衡量压缩的效率,一般用1减去压缩数据大小除以原始数据大小的值。这个值称为数据的压缩率。学习

从广义上讲,数据压缩的方法分为两大类:有损压缩和无损压缩。在有损压缩中,咱们接受数据有必定的损失来换取更大的压缩比。在某些应用中,必定的损失是能够接受的,好比图像或音频的处理,由于这种损失不会影响其效果而且会受到严格控制。然而,咱们一般使用的是无损压缩,它可以保证解压缩时准确的还原原始数据。编码

咱们重点介绍无损压缩,实现无损压缩主要有两种方法:最小冗余编码和基于字典的方法。最小冗余编码使用更少的位对出现更为频繁的字符进行编码,用较长的位对出现频繁较低的字符进行编码。在基于字典的方法中,其经过对数据进行符号编码,来代替那些重复多余的短语。加密

数据压缩的重要组成部分

位操做是数据压缩的重要组成部分,由于绝大多数方法在某种程度上都须要对数据的位进行操做。C语言自己提供了一些位操做的接口,能够用这些接口来实现一些扩展的位操做类。spa

咱们先来看一下数据压缩的头文件(在数据压缩介绍中必不可少,包括各类符号常量、压缩、解压缩的接口等):指针

/*compress.h 数据压缩的头文件*/ #ifndef COMPRESS_H #define COMPRESS_H #include "bitree.h"

/*定义霍夫曼树的节点数据结构*/ typedef struct HuffNode_ { unsigned char symbol; int freq; }HuffNode; /*定义霍夫曼代码表中条目的数据结构*/ typedef struct HuffCode_ { unsigned char used; unsigned char code; unsigned char size; }HuffCode; /*定义LZ77令牌成员所须要的位数*/
#define LZ77_TYPE_BITS   1
#define LZ77_WINOFF_BITS 12
#define LZ77_BUFLEN_BITS 5
#define LZ77_NEXT_BITS   8

/*定义滑动窗口的大小和LZ77的超前缓冲区. 每一个都必须小于或等于2,分别提升到LZ77_WINOFF_BITS和LZ77_BUFLEN_BITS。 */
#define LZ77_WINDOW_SIZE  4096
#define LZ77_BUFFER_SIZE  32 

/*定义LZ77短语标记的位数*/
#define LZ77_SYMBOL_BITS  (LZ77_TYPE_BITS + LZ77_WINOFF_BITS + LZ77_NEXT_BITS + LZ77_BUFLEN_BITS)

/*函数接口*/
int huffman_compress(const unsigned char *original, unsigned char **compressed, int size); int huffman_uncompress(const unsigned char *compressed, unsigned char **original); int lz77_compress(const unsigned char *original, unsigned char **compressed, int size); int lz77_uncompress(const unsigned char *compressed, unsigned char **original); #endif // COMPRESS_H

位操做的描述

在压缩和解压缩数据时,经常要在小于一个字节的数量级上进行数据操做。因此,在学习各类数据压缩的方法以前,必须熟悉一些对数据位进行的操做,这很是重要。本节展现的方法包含了对任意位的缓冲区的操做。固然,这里介绍的位操做只是一部分,只是定义了如今数据压缩和后续的数据加密中所要用到的操做。code

位操做的接口定义

bit_getblog


int bit_get (const unsigned char *bits, int pos);索引

返回值:相应位所处的状态:0或1。

描述:获取缓冲区bits中处于位置pos的位的状态。缓冲区最左边的位置为0。返回的状态值为0或1。

复杂度:O(1)

bit_set


void bit_set (unsigned char *bits, int pos, int state);

返回值:无

描述:设置缓冲区bits中处于位置pos的位的状态(根据state值来设置)。缓冲区最左边的位设置为0。状态值必须为0或1。

复杂度:O(1)

bit_xor


void bit_xor (const unsigned char *bits1, const unsigned char *bits2, unsigned char *bitsx, int size);

返回值:

描述:按位计算两个缓冲区bits1和bits2的异或值,其中每一个缓冲区包含size个位,而后将结果返回bitsx中。异或的过程是将两个二进制操做数进行运算,若是操做数据处于位置i的两位相同,获得0;若是处于位置i的两位不一样,则返回1。例如:11010 异或 01011 = 10001。bitsx所须要的存储空间由函数调用者来管理。

复杂度:O(B),其中B为每一个缓冲区中位的个数。

bit_rot_left


void bit_rot_left (unsigned char *bits, int size, int count);

返回值:无

描述:轮转缓冲区bits(含size位),将位值向左移动count位。此操做完成后,处于最左端的count位移动到缓冲区的最右端,并且其余的位也相应的轮转。

复杂度:O(nB),其中B为每一个缓冲区中位的个数,n为要轮转到左边的位数。

位操做的实现与分析

每一个位操做均可操做缓冲区中的数据,缓冲区由无符号字符做为指针来指定。该指针指向足够多的字节来表示缓冲区中的位数。若是缓冲区中的位数不是8的倍数,那么说明最后一个字节的某些位没有使用。

bit_get

bit_get操做获取缓冲区中一个位的状态。要作到这一点,首先要肯定位所在的字节,而后经过一个掩码从字节中获取相应的位。掩码中设置为1的位是将要从字节中读出的位,用一个循环操做将此位移动到适当的位置,经过索引bits中相应的字节,并应用调用后的掩码,能够获取所需的位。

bit_get的时间复杂度为O(1)。这是由于获取缓冲区中的位的状态所进行的操做都可以在固定的时间内完成。

bit_set

bit_set 操做设置缓冲区中的一个位的状态。此操做与bit_get的工做方式类似,只是它是利用掩码设置指定的位的状态,而bit_get是获取指定位的状态。

bit_set的时间复杂度为O(1)。这是由于获取缓冲区中位的状态所进行的全部操做都可以在固定的时间内完成。

bit_xor

bit_xor对两个缓冲区bits1和bits2进行按位异或运算,并将计算的结果放到缓冲区bitsx中。要作到这一点,将bits1中第i个位置的位与bits2中第i个位置的位进行比较,若是位值相同,将第i个位置的位置为0;不然,将第i个位置的位置为1。这个过程持续下去直到缓冲区中size指定的每一个位都计算完成。

bit_xor的时间复杂度为O(B),其中B是每一个缓冲区中的位数。这是由于此操做要在每一个位上循环迭代一次。

bit_rot_left

bit_rot_left将缓冲区指定数量的位向左轮转。首先,保存最左端字节的最左端的位,而后向左一位一位地移动每一个字节的位值。在移动字节的过程当中,将前一个字节最右边的位移动到当前字节的最左边。当处理到最后一个字节时,将其最右边的位移动到首字节的最高位上。这个过程一直持续下去直到全部的位都轮转到位。

bit_rot_left的时间复杂度为O(nB),其中n为要向左轮转的位的个数,B是缓冲区中位的个数。这是由于,对于每次轮转,要进行(B/8)+1次移动。

 示例:位操做的实现

/*bit.c 位操做的实现*/ #include <stdlib.h> #include "bit.h"

/*bit_get 获取缓冲区bits中处于pos位的状态*/
int bit_get(const unsigned char *bits, int pos) { unsigned char mask; int i; /*设置掩码*/ mask = ox80; for(i=0; i<(pos % 8); i++) mask = mask >> 1; /*用位与运算获取对应的位*/
    return (((mask & bits[(int)(pos / 8)]) == mask)? 1:0) } /*bit_set 设置缓冲区bits中位于pos位的状态*/
void bit_set(unsigned char *bits, int pos, int state) { unsigned char mask; int i; /*设置掩码*/ mask = ox80; for(i=0; i<(pos % 8); i++) mask=mask>>1; /*依据state设置位*/
    if(state) bits[pos/8] = bits[pos/8] | mask; else bits[pos/8] = bits[pos/8] & (~mask); return; } /*bit_xor 按位异或运算*/
void bit_xor(const unsigned char *bits1,const unsigned char *bits2,unsigned char *bitsx,int size) { int i; /*计算两个缓冲区的按位异或*/
    for(i=0;i<size;i++) { if(bit_get(bits1,i) != bit_get(bits2,i)) bit_set(bitsx,i,1); else bit_set(bitsx,i,0); } return; } /*bit_rot_left 轮转缓冲区bits(含size位),将位值向左移count位*/
void bit_rot_left(unsigned char *bits,int size,int count) { int fbit,lbit,i,j; /*将缓冲区向左轮转指定位数*/
    if(size > 0) { for(j=0; j<count; j++) { for(i=0; i<=((size-1)/8); i++) { /*得到要从当前字节偏移的位*/ lbit = bit_get(&bit[i],0); if(i==0) { /*保存要从首字节移动到后面的位*/ fbit = lbit; } else { /*将前一字节最右边的位设置为当前字节最左边的位*/ bit_set(&bits[i-1],7,lbit); } /*将当前字节向左移动*/ bits[i] = bits[i] << 1; } /*将缓冲区最右边的位设置为从第一个字节偏移的位*/ bit_set(bits,size-1,fbit); } } return; }
相关文章
相关标签/搜索