aba
刚加入词典,下一个编码立刻就用到了它,又因为编码比解码快一步,这将导致在解码时,遇到码字259
时,词典中并没有对应字符串,无法解码。aba
这样的头尾字符相同的字符串才会出现这种问题,因此在解码时,遇到无法解出码字的情况时,按照流程图中所说,将PW
对应字符串和PW
对应字符串的第一个字符拼接即可解出,并将其加入词典。bitio.h
头文件声明了本次实验中要用到的结构体和方法函数。
/*
* Declaration for bitwise IO
*
* vim: ts=4 sw=4 cindent
*/
#ifndef __BITIO__
#define __BITIO__
#include
typedef struct{
FILE *fp;
unsigned char mask;
int rack;
}BITFILE;
BITFILE *OpenBitFileInput( char *filename);
BITFILE *OpenBitFileOutput( char *filename);
void CloseBitFileInput( BITFILE *bf);
void CloseBitFileOutput( BITFILE *bf);
int BitInput( BITFILE *bf);
unsigned long BitsInput( BITFILE *bf, int count);
void BitOutput( BITFILE *bf, int bit);
void BitsOutput( BITFILE *bf, unsigned long code, int count);
#endif // __BITIO__
main
函数主函数即为实际的编解码过程。
在调试属性内更改命令参数。第一个参数为E时,进行编码操作;第一个参数为D时,进行解码操作。
int main(int argc, char** argv) {
FILE* fp;
BITFILE* bf;
if (4 > argc) {
fprintf(stdout, "usage: \n%s \n" , argv[0]);
fprintf(stdout, "\t: E or D reffers encode or decode\n" );
fprintf(stdout, "\t: input file name\n" );
fprintf(stdout, "\t: output file name\n" );
return -1;
}
if ('E' == argv[1][0]) { // do encoding
fp = fopen(argv[2], "rb");
bf = OpenBitFileOutput(argv[3]);
if (NULL != fp && NULL != bf) {
LZWEncode(fp, bf);
fclose(fp);
CloseBitFileOutput(bf);
fprintf(stdout, "encoding done\n");
}
}
else if ('D' == argv[1][0]) { // do decoding
bf = OpenBitFileInput(argv[2]);
fp = fopen(argv[3], "wb");
if (NULL != fp && NULL != bf) {
LZWDecode(bf, fp);
fclose(fp);
CloseBitFileInput(bf);
fprintf(stdout, "decoding done\n");
}
}
else { // otherwise
fprintf(stderr, "not supported operation\n");
}
return 0;
}
OpenBitFileInput
函数和OpenBitFileOutput
函数二进制输入文件和输出文件的打开。
BITFILE *OpenBitFileInput( char *filename){
BITFILE *bf;
bf = (BITFILE *)malloc( sizeof(BITFILE));
if( NULL == bf) return NULL;
if( NULL == filename) bf->fp = stdin;
else bf->fp = fopen( filename, "rb");
if( NULL == bf->fp) return NULL;
bf->mask = 0x80;
bf->rack = 0;
return bf;
}
BITFILE *OpenBitFileOutput( char *filename){
BITFILE *bf;
bf = (BITFILE *)malloc( sizeof(BITFILE));
if( NULL == bf) return NULL;
if( NULL == filename) bf->fp = stdout;
else bf->fp = fopen( filename, "wb");
if( NULL == bf->fp) return NULL;
bf->mask = 0x80;
bf->rack = 0;
return bf;
}
LZWEncode
函数void LZWEncode(FILE* fp, BITFILE* bf) {
int character;
int string_code;
int index;
unsigned long file_length;
fseek(fp, 0, SEEK_END);
file_length = ftell(fp);
fseek(fp, 0, SEEK_SET);
BitsOutput(bf, file_length, 4 * 8);
InitDictionary();
string_code = -1;
while (EOF != (character = fgetc(fp))) {
index = InDictionary(character, string_code);
if (0 <= index) { // string+character in dictionary
string_code = index;
}
else { // string+character not in dictionary
output(bf, string_code);
if (MAX_CODE > next_code) { // free space in dictionary
// add string+character to dictionary
AddToDictionary(character, string_code);
}
string_code = character;
}
}
output(bf, string_code);
}
InitDictionary
函数void InitDictionary(void) {
int i;
for (i = 0; i < 256; i++) {
dictionary[i].suffix = i;
dictionary[i].parent = -1;
dictionary[i].firstchild = -1;
dictionary[i].nextsibling = i + 1;
}
dictionary[255].nextsibling = -1;
next_code = 256;
}
InDictionary
函数character
是否在词典中。 int InDictionary(int character, int string_code) {
int sibling;
if (0 > string_code) return character;
sibling = dictionary[string_code].firstchild;
while (-1 < sibling) {
if (character == dictionary[sibling].suffix) return sibling;
sibling = dictionary[sibling].nextsibling;
}
return -1;
}
BitOutput
函数#define output(f, x) BitsOutput( f, (unsigned long)(x), 16)
void BitOutput( BITFILE *bf, int bit){
if( 0 != bit) bf->rack |= bf->mask;
bf->mask >>= 1;
if( 0 == bf->mask){ // eight bits in rack
fputc( bf->rack, bf->fp);
bf->rack = 0;
bf->mask = 0x80;
}
}
AddToDictionary
函数void AddToDictionary(int character, int string_code) {
int firstsibling, nextsibling;
if (0 > string_code) return;
dictionary[next_code].suffix = character;
dictionary[next_code].parent = string_code;
dictionary[next_code].nextsibling = -1;
dictionary[next_code].firstchild = -1;
firstsibling = dictionary[string_code].firstchild;
if (-1 < firstsibling) { // the parent has child
nextsibling = firstsibling;
while (-1 < dictionary[nextsibling].nextsibling)
nextsibling = dictionary[nextsibling].nextsibling;
dictionary[nextsibling].nextsibling = next_code;
}
else {// no child before, modify it to be the first
dictionary[string_code].firstchild = next_code;
}
next_code++;
}
LZWDecode
函数void LZWDecode(BITFILE* bf, FILE* fp) {
int character;
int new_code, last_code;
int phrase_length;
unsigned long file_length;
file_length = BitsInput(bf, 4 * 8);
if (-1 == file_length) file_length = 0;
/*需填充*/
InitDictionary();//初始化词典
last_code = -1;
while (0 < file_length) {
new_code = input(bf);
if (new_code >= next_code) { // this is the case CSCSC( not in dict)
d_stack[0] = character;
phrase_length = DecodeString(1, last_code);
}
else {
phrase_length = DecodeString(0, new_code);
}
character = d_stack[phrase_length - 1];
while (0 < phrase_length) {
phrase_length--;
fputc(d_stack[phrase_length], fp);
file_length--;
}
if (MAX_CODE > next_code) {// add the new phrase to dictionary
AddToDictionary(character, last_code);
}
last_code = new_code;
}
}
BitsInput
函数unsigned long BitsInput( BITFILE *bf, int count){
unsigned long mask;
unsigned long value;
mask = 1L << (count-1);
value = 0L;
while( 0!=mask){
if( 1 == BitInput( bf))
value |= mask;
mask >>= 1;
}
return value;
}
BitInput
函数:int BitInput( BITFILE *bf){
int value;
if( 0x80 == bf->mask){
bf->rack = fgetc( bf->fp);
if( EOF == bf->rack){
fprintf(stderr, "Read after the end of file reached\n");
exit( -1);
}
}
value = bf->mask & bf->rack;
bf->mask >>= 1;
if( 0==bf->mask) bf->mask = 0x80;
return( (0==value)?0:1);
}
Input
函数int
型后的BitsInput
函数#define input(f) ((int)BitsInput( f, 16))
DecodeString
函数int DecodeString(int start, int code) {
int count;
count = start;
while (0 <= code) {
d_stack[count] = dictionary[code].suffix;
code = dictionary[code].parent;
count++;
}
return count;
}
CloseBitFileOutput
函数二进制文件关闭。
void CloseBitFileOutput( BITFILE *bf){
// Output the remaining bits
if( 0x80 != bf->mask) fputc( bf->rack, bf->fp);
fclose( bf->fp);
free( bf);
}
以一个简单的txt文件为例:
可以看到编码效果非常明显,编码成功。
对刚刚编码所得到的二进制文件1out.dat
进行解码
可以看到经过解码过后与原来编码前的内容一样,大小一样,解码成功。
本次实验选择了十种不同格式类型的文件,使用LZW编码器进行压缩得到输出的压缩比特流文件。
压缩前后文件大小如下:
文件格式 | 压缩前大小 | 压缩后大小 | 压缩率 |
---|---|---|---|
.txt | 195 KB | 6 KB | 3.08% |
.bmp | 1519 KB | 908 KB | 59.78% |
.png | 298 KB | 384 KB | 128.86% |
.jpg | 63 KB | 91 KB | 144.44% |
.csv | 74935 KB | 7388 KB | 9.86% |
.docx | 1022 KB | 1273 KB | 124.56% |
.md | 4 KB | 3 KB | 75.00% |
.rgb | 192 KB | 179 KB | 93.23% |
.yuv | 96 KB | 69 KB | 71.88% |
3818 KB | 4605 KB | 120.61% |
观察发现,十个文件中,有四个文件在LZW编码后反而变得更大了。
分别查阅了这四种格式文件的资料发现:
png是一种采用无损压缩算法的位图格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小。
JPEG压缩技术十分先进,它可以用有损压缩方式去除冗余的图像数据,换句话说,就是可以用较少的磁盘空间得到较好的图像品质。
txt csv
等文件类型,而对于图片类,已压缩的图片无法起到压缩效果,反而会使文件增大。