您的位置:首页 > 编程语言 > Java开发

java数字字符串压缩

2015-09-29 09:27 369 查看
跟同事讨论一个问题,将20个十进制数的数字字符组成的字符串尽量压缩。

我首先想到从bit位层面考虑压缩,毕竟压缩后的东西一般不能直接用于表达,只是用于传输或存储。java的一个字符占2字节16bit位,但表示一个十进制数只需要4bit位就够了。在这种思路下,采用位截断压缩,可以把四个字符压缩到一个字符中。

public class CompressDigitalString {

//以String中的char元素为一个基本单位,16bits,存储4个数字的4bits。以下数组用空间换时间,分别构造出数字0~9的4bit位存储模型
static final char LOW_LOW_BYTES[] = {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009};    //最右边的4位
static final char LOW_HIGH_BYTES[] = {0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, 0x0090};    //倒数第二个右边的4位
static final char HIGH_LOW_BYTES[] = {0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700, 0x0800, 0x0900};    //倒数第三个右边的4位
static final char HIGH_HIGH_BYTES[] = {0x0000, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000, 0x9000};    //最左边的4位
static final char BYTES[][] = {HIGH_HIGH_BYTES, HIGH_LOW_BYTES, LOW_HIGH_BYTES, LOW_LOW_BYTES};    //为了方便做循环,将上面的数组拼在一起

public static String compress(String num) {
int size = num.length();
size = ( size & 0x03 ) != 0 ? size >> 2 + 1 : size >> 2;    //size&0x03表示对4取模,判断是否整除4。计算最后返回String类型字符长度
StringBuilder temp = new StringBuilder(size);
char c = 0x0000;
for(int i = 0; i < num.length(); ++i) {    //对String从左到右处理,左边的可以认为是字串的高位
if ((i & 0x03) == 0 && i != 0) {    //每处理完String的4个字符就做一次拼接,但最后一次的此处拼接不了
temp.append(c);
c = 0x0000;    //恢复到原始状态,等待下一次拼接
}
c |= BYTES[i & 0x03][Integer.valueOf(String.valueOf(num.charAt(i)))];    //将逐数字在各自的位模型用 '或' 操作
}
return temp.append(c).toString();    //处理最后一次拼接,然后转化成String并返回
}
//输入字符串为 "1234",计算后返回字符串的内存结构(16进制)为0x1234,也即是 0001 0010 0011 0100,但是这个字符不可见...
}


这是一种非常简单原始的想法,需要注意,由于没有对原始串的长度计数,复原时不知道该恢复成多少位。但作为如此简单而且压缩效率尚可的方式,这个方法值得改进后使用。

后来同事又提出来一个要求,压缩出来的字符串不能太任意,字符必须在0~9a~zA~Z这62个字符中。然后我就犯难了。上面描述的方式相当于每个char能够使用10^4种形态(每4bit可以有0~9共10种形态),而这个要求使得每个char只有62种形态,就压缩程度说来要大打折扣。进制转换的思路能够比较容易实现这个需要,但压缩出来的字符串长度会比较大:

log(10^20)/log(62)=11.1582...


需要12位,而且还需要自己实现10进制和62进制的转化。有个便利的做法,利用java内置的BigIngeter来完成10进制数转化36(0~9a~z)进制数。

BigInteger test = new BigInteger("12313213");
String ret = test.toString(36);
//压缩位数 log(10^20)/log(36)=12.8509...


从压缩结果上看,只相差1位,但是编码量会小很多,而且不容易出错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 压缩