26个字母压缩编码

一 字节压缩
 
实现原理:因为a~z的ascii码用不了一个字节表示,减小‘a’后每个字符实际上代表了0-26,五个bit即可表示,压缩后,能省 3/8 的空间, 关键在于如何组织存储顺序便于检索和存储,同时要考虑存储的时间效率
如果将五位按照顺序依次存储,一不好保存,二不好访问
访问按照字节进行,应尽量将 m个数组合成n个字节,能否将8 个字符存成 5 个字节呢?
 
因此考虑就5位再拆下,分解成最高位和低四位,这样每两个字符就可以转换成一个新的数了; 8 个字符的最高位可组合成一个字节 ;4、8的规律性很强,组合分解都很方便
 
××××××××××××××××××××××××××××××××
 
#define LOWMASK 0x0f
#define HIGHMASK (1<<4)
#define CHARLEN 7
 
输入参数:inarray[] 待编码的字符数组;inlen数组的长度
输入参数:outarray [] 编码后的字符数组;outlen编码后数组的长度
// unsigned char outarray[] 编码后的字符数组用到了最高位,是有效位,非符号位
void   CodeChar(const char inarray[],unsigned int inlen, unsigned char outarray[ ], unsigned int &outlen)
{
       int InIndex = 0;
       int OutIndex = 0;
       int CompoudIndex = 0;
       int CompoudValue = 0;
       int tempchar = 0;
 
       while(inlen--)
       {
              tempchar = inarray[InIndex] - 'a'; // 将字符换成数字0-26
 
              if(InIndex%2 == 0)              // 偶数位存放在每个字符的高位
                     outarray[OutIndex] = ( tempchar & LOWMASK) << 4;
              else         // 奇数位存放在每个字符的低位
                     outarray[OutIndex] |=(tempchar & LOWMASK);
             
              if((InIndex%2 == 1) && (InIndex != 0)) // 存放完两个数后,增加输出数组的下标
                     OutIndex++;
             
              // 将每个字符的第五位存放起来, 0 7 分别对应此字符的第 7 -第 0
              CompoudValue |= ((tempchar & HIGHMASK) << 3) >> (InIndex%8);
 
              CompoudIndex++;
              if(CompoudIndex == sizeof(char) * 8) // 存放了八个数后,将合成值保存到输出数组
              {    
                     CompoudIndex = 0;
                     outarray[OutIndex] = CompoudValue;
                     OutIndex++;
              }
              InIndex++;
       }
 
       if(InIndex%2 == 1)       // 某个输出字符只用了高四位,但保存合成值的输出下标未增加
       OutIndex++;
 
       if(CompoudIndex != 0) // 此时说明输入数组个数非 8 的倍数,最后一个合成值上面未保存
       {
                     outarray[OutIndex] = CompoudValue;
                     OutIndex++;
       }
 
       outlen =OutIndex; // 保存转换完毕后的输出数组长度
}
 
// 输入参数:inarray 上面编码后的数组;inlen编码后的数组长度;outk待解码字符在原数组中的下标位置
// 输出参数:outchar解码的码值;inlen编码后的数组长度
void   DecodeChar(const unsigned char inarray[],unsigned int inlen, unsigned int outk, char &outchar)
{
 
       int CompoudIndex = 0;
       int HighValue = 0;          // 待解码字符的第五位(转换为0-26之后的)
       int LowValue = 0;          // 待解码字符的低四位(转换为0-26之后的)
       int CompoudValue = 0;   // 解码后的值
 
       if(((outk+8)/8 * 5 - 1) < inlen)      // 待解码字符的最高位不在最后一个合成值内
              HighValue = ((inarray[((outk+8)/8 * 5 - 1 )] >> (CHARLEN - (outk%8))) & 0x01) << 4;
       Else // 待解码字符的最高位在最后一个合成值内
       {
              HighValue = ((inarray[inlen-1 ] >> (CHARLEN - (outk%8))) & 0x01) << 4;
       }
 
 
       if(outk%2 == 0)     // 偶数保存在高位
              LowValue = inarray[outk/2+outk/8 ] >> 4;
       else
              LowValue = inarray[outk/2+outk/8] & LOWMASK;
 
       CompoudValue = HighValue | LowValue;       // 解码后的数字
       CompoudValue += 'a';    // 解码后的字符
 
       outchar = CompoudValue;
}
 
 
void   main(void)  
{  
       char *in = "abcdefghijklmnuvw";
       unsigned char out[100] = {0};
       int outindex = 0;
       unsigned int outcount;
       int incount = strlen(in);
       float coderatio = 0;
       char outchar;
      
       while(outindex < 100)
              out[outindex++] = 0;
 
       CodeChar(in,incount, out, outcount);
       out[incount] = '/0'; // 编码
 
       cout<<outcount<<endl;
       printf("%s",out);
 
       coderatio = (float )outcount/incount;
       printf("%6f",coderatio);
 
       outindex = 0; // 解码
       while(outindex < incount)
       {
       DecodeChar(out,outcount,outchar,outindex);
       cout<<outchar<<endl;
       outindex++;
       }
 
}
 
××××××××××××××××××××××××××××××××
××××××××××××××××××××××××××××××××
 
二  26进制编码
    26 个字母就当作 26 进制 ,然后每6个字母转换为一个long,32位的 4 个字节..  压缩 1/3 
0-26个当作26进制对应的26个数符, 6 个字符组成的字符串当作一个 26 进制的数 .,转换为10进制整数,刚好范围可以被32 long 容纳 ,不过存在一点点浪费.  
比如a代表1,b代表2 ,ab代表26进制的12, 即十进制的28;
 
反过来从long X得到原始的字符
X/26^5 即得到六个字符的高位,依次类推;但对于最后一个整型数应该特殊考虑。
就相当于十六进展和十进制的转换问题
 
××××××××××××××××××××××××××××××××
 
#define AZMAX 26
//输入参数:inarray[] 待编码的字符数组;inlen数组的长度
//输入参数:outarray [] 编码后的整型数组; outlen编码后数组的长度
// unsigned int outarray[] 编码后的整型数组用到了最高位,是有效位,非符号位
void AzCodeChar(const char inarray[],unsigned int inlen, unsigned int outarray[], unsigned int &outlen)
{
       int InIndex = 0;
       int OutIndex = 0;
       unsigned int CompoudValue = 0;
 
       while(inlen--)
       {
              CompoudValue = CompoudValue* AZMAX + (inarray[InIndex] - 'a'); // 将字符换成数字0-26
              InIndex++;
              // 按照字符的顺序依次乘 26 ,即把各个数对应的值的和求出来了
 
              if(InIndex%6 == 0)              // 六个字符转化为一个整型数
              {
                     outarray[OutIndex] = CompoudValue;
                     CompoudValue = 0;
                     OutIndex++;
              }           
       }
 
       if(InIndex%6 != 0) // 不足六个字符,上面未保存
       {
                     outarray[OutIndex] = CompoudValue;
                     CompoudValue = 0;
                     OutIndex++;
       }
 
       outlen =OutIndex; // 保存转换完毕后的输出数组长度
}
 
// 输入参数:inarray 上面编码后的数组;inlen编码后的数组长度;outindex待解码字符在原数组中的下标位置;outlen 待解码字符的原数组总长度(比按 5 位编码多了一个参数)
// 输出参数:outchar解码的码值
void   AzDecodeChar(const unsigned int inarray[],unsigned int inlen, unsigned int outindex, unsigned int outlen, char &outchar)
{
 
       int DecodeValue = 0;      // 解码后的值
       unsigned int DevideIndex = 0;
       unsigned int DevideValue = 1;
 
       if(outindex < (inlen - 1)*6)    // 待解码字符不在最后一个合成值内
       {    
              DevideIndex = 5 - outindex%6;     // 得到待除的数基于26的幂指数            
       }
       else // 待解码字符在最后一个合成值内
       {
              DevideIndex = 5 - (inlen*6 - outlen) - outindex%6; // 此时最高位的意义变小了
       }
 
       while(DevideIndex > 0)
       {
              DevideValue *= AZMAX;       // 得到待除的数26^ DevideIndex
              DevideIndex--;
       }
 
       DecodeValue   = (inarray[outindex/6]/DevideValue)%AZMAX; // 得到当前的个位数
 
       outchar = DecodeValue + 'a';
}
 
void   main(void)  
{  
       char *in = "abcdefghijklmnuvw";
       unsigned int out[100] = {0};
       int outindex = 0;
       unsigned int outcount;
       int incount = strlen(in);
       float coderatio = 0;
       char outchar;
      
       while(outindex < 100)
              out[outindex++] = 0;
 
       AzCodeChar(in,incount, out, outcount);
       cout<<outcount<<endl;
 
       coderatio = (float)outcount * sizeof(int) /incount;
       printf("%6f/n",coderatio);
 
       outindex = 0; // 解码
       while(outindex < incount)
       {
       AzDecodeChar(out, outcount, outindex, incount, outchar);
       cout<<outchar<<endl;
       outindex++;
       }
 
}
 
××××××××××××××××××××××××××××××××
××××××××××××××××××××××××××××××××
 
细心的读者可能会发现,上述将字符串看成26进制的字符串,然后再将其编码为十进制,即int类型存起来;
而对于“12356819 ”类型的十进制的字符串转化为十进制的 int数,不就是常考的atio(char *in) 函数么? 考虑过“011000010100”二进制的字符串么?八进制“ 23104762 ”呢?
相信这个时候你应该明白了26进制字符串“adkjflmgdsjkflkmgzxy”的存储转换原理了吧
而其解码过程不就是 itoa(int k, char *out) 函数 么?
有兴趣的朋友可以参考前面的帖子“ 整数转换成字符串 看算法的联想

转载于:https://www.cnblogs.com/yiql2012/p/3259551.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值