概述
在存储数据的时候,我们大部分的时候都是在存储数字。如何高效且用最少的字节的来存储这是个有很多技巧的问题。
现总结些经验如下.
一, 定长存储,就是每个值用等长的字节数来存储。这种方法是显而易见的。
1,确定存储的数的最大值,确定用多少个字节就可以存储。比如存储的数值最大不会超过256,这是用1个字节来存储将是最划算。如果最大值不会超过0xFFFFFF,用3个字节存储最划算。
2,当用多字节来存储的时候,固定的字节流顺序存储可以解决大端小端对齐不一致问题,强烈提醒不要直接把一个int型写到文件存储,除非你保证一辈子都不关心大端小端对齐问题。比如存储3个字节长度的数值代码:
static void pack_uint24(UINT8 *addr, UINT32 u24 )
{
assert(u24 <= 0xFFFFFF);
addr[0] = (u24 >> 0) & 0xFF;
addr[1] = (u24 >> 8) & 0xFF;
addr[2] = (u24 >> 16) & 0xFF;
}
即总是先存储低字节,后存储高字节。解析的时候代码:
static UINT32 unpack_uint24( const UINT8 addr[3] )
{
return ((UINT32)addr[0] << 0) |
((UINT32)addr[1] << 8) |
((UINT32)addr[2] << 16);
}
二, 变长存储,用不固定的字节数来存储数值。这样比固定字节存储节省很多空间。
1,用1-2个字节存储不大于0x7FFF的数值。通过第一个字节的最高位来确定是1个字节还是2个字节,当最高位为0,表示一个字节存储,当最高位为1,表示2个字节存储。
// msb format
// ---------------
// 0*,* ..... 1 byte (1 + 7 bits) 表示的数值范围为0x0 ~ 0x7F
// 1*,* ..... 2 bytes (1 + 15 bits) 表示的数值范围为0x0 ~ 0x7FFF
这里面还有个技巧,就是当msb为1的时候,也覆盖了0 ~ 0x7F的数值范围。为了表示更广泛的范围,可以把当msb为1的时候的数值范围修改为:0x80 ~ 0X807F,即当是2个字节表示的时候,得到的数值加上0x80为真实的存储数值。
// msb format
// ---------------
// 0*,* ..... 1 byte (1 + 7 bits) 表示的数值范围为0x0 ~ 0x7F
// 1*,* ..... 2 bytes (1 + 15 bits) 表示的数值范围为0x80 ~ 0x807F
2, 用1-3个字节存储不大于0x3FFFFF的数值,由于字节数有3种情况(1个字节,2个字节,3个字节),所以需要预留2bit出来描述字节数。
//
// msb format
// ---------------
// 0* ..... 1 byte (1 + 7 bits) 表示的数值范围为0x0 ~ 0x7F
// 10* ..... 2 bytes (2 + 14 bits) 表示的数值范围为0x0 ~0x3FFF
// 11* ..... 3 bytes (2 + 22 bits) 表示的数值范围为0x0 ~ 0x3FFFFF
//
同样的有上面的技巧,通过修正2字节和3字节的表示范围,来覆盖大那么一点点的数值范围。
3,上面总是用最少的bit位来描述字节数,其实也没有必要一定要留出某个bit来描述字节数信息。可以预留出某个数值来表示字节数。比如,
//
// msb format
// ---------------
// 0x0000 - 0x00EF ..... 1 byte 表示的数值范围为0x0 ~ 0xEF
// 0xF000 - 0xFFFF ..... 2 bytes 表示的数值范围为0x0 ~ 0x0FFF
//
我们把第一个字节的搞4个bit来描述字节数,当高4个字节为0 - E的时候,表示1个字节存储,描述范围为 0 ~ 0xEF,当高4bit为F,后12bit表示真实的数值,表示范围为 0 - 0x0FFF.这种表示方法和第一种表示方法的区别在于,此方法表示的数值范围更小,但是用1个字节表示范围更宽,既牺牲2字节的表示范围来扩展1个字节的表示范围。
变长存储的效用体现在小数值出现的频率很高,这样用1个字节就可以存储大部分情况,而少部分情况需要2个字节来存储。比如,我们需要存储name表的时候,2个name之间其实有很多重复的字符。每个name都把自己完整的字符串存起了将非常大,通过token来存储就可以起到很好的压缩功能,具体做法:
每一个name由token组成,每一个token由具体的字符组成,其中,每一个token有自己唯一的id来标示,而name就由一系列的token id来组成。如何对token取id,我们把token按出现的次数来排序,出现的次数越多,就排在越前面,他的token id就越小。name中的token id就可以用变长来存储了。这时候,根据token 总数可以确定是用第一种变长还是第二种变长,当token的总数不超过0x0FFF个的时候,第三种变长可以得到更好的压缩率,因为1个字节可以表示更多的小id范围,有个事实就是id越小,出现的频率越高,说明用1个字节的地方更多。
最后
以上就是无奈绿茶为你收集整理的如何压缩存储数字一, 定长存储,就是每个值用等长的字节数来存储。这种方法是显而易见的。二, 变长存储,用不固定的字节数来存储数值。这样比固定字节存储节省很多空间。的全部内容,希望文章能够帮你解决如何压缩存储数字一, 定长存储,就是每个值用等长的字节数来存储。这种方法是显而易见的。二, 变长存储,用不固定的字节数来存储数值。这样比固定字节存储节省很多空间。所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复