假设我有String input = "1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,0,4,0,0,0,4,0,3";
我想把它编码成一个字符较少的字符串,实际上隐藏了实际的信息通过表示它在罗马字符,IE。上面的代码编码为类似"Adqwqkjlhs"
的东西。如果给定已编码的字符串,必须能够解码为原始字符串。
字符串输入实际上是我从URL的哈希中解析出来的东西,但原始格式很长,可以进行操作。
任何想法?
感谢编辑# 1数字可以是0到99,每个数字之间用逗号分隔,用于String.split(",")检索String[]
编辑#2(编码字符串的目的)
假设上面的字符串编码为bmtwva1131gpefvb1xv
,那么我可以有像www.shortstring.com/input#bmtwva1131gpefvb1xv
这样的URL链接。从那里,我将bmtwva1131gpefvb1xv
解码成逗号分隔的数字。
这与Nathan Hughes的解决方案相比并没有太大的改进,但是字符串越长,节省的时间就越多。
编码:创建一个以"1"开头的字符串,使源字符串中的每个数字都是2位数字,因此"0"变成"00","5"变成"05","99"变成"99",等等。以36为基数表示结果数。
解码:将以36为基数的数字/字符串改回以10为基数,跳过第一个"1",然后将每2个数字/字母转换为int并重建原始字符串。
示例代码:
String s = "1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,0,4,0,0,0,4,0,3";
// ENCODE the string
StringTokenizer tokenizer = new StringTokenizer(s,",");
StringBuilder b = new StringBuilder();
b.append("1"); // This is a primer character, in case we end up with a bunch of zeroes at the beginning
while(tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
if(token.length()==1) {
b.append("0");
b.append(token);
}
else {
b.append(token);
}
}
System.out.println(b);
// We get this String: 101020000000000000000000000000000000000010202030004000000040003
String encoded = (new BigInteger(b.toString())).toString(36);
System.out.println(encoded);
// We get this String: kcocwisb8v46v8lbqjw0n3oaad49dkfdbc5zl9vn
// DECODE the string
String decoded = (new BigInteger(encoded, 36)).toString();
System.out.println(decoded);
// We should get this String: 101020000000000000000000000000000000000010202030004000000040003
StringBuilder p = new StringBuilder();
int index = 1; // we skip the first "1", it was our primer
while(index<decoded.length()) {
if(index>1) {
p.append(",");
}
p.append(Integer.parseInt(decoded.substring(index,index+2)));
index = index+2;
}
System.out.println(p);
// We should get this String: 1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,0,4,0,0,0,4,0,3
我不知道怎么把一个大数化为64进制。仔细选择的符号(如+,,-)可以被URL编码,所以0-9,a-z, a-z,加上""one_answers"-"是64。BigInteger.toString()方法只占用字符。MAX_RADIX为36(无大写字母)。如果你能找到一种方法,将一个大的数字改为64进制,那么最终编码的字符串将更短。
编辑:看起来这是为你做的:http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html
如何将其保存为基数36 ?
在Java中应该是
new java.math.BigInteger("120000000000000000012230400403").toString(36)
计算结果为"bmtwva1131gpefvb1xv"
用
你会得到原来的数字new java.math.BigInteger("bmtwva1131gpefvb1xv", 36)
这是一个很好的点,它不处理前导0 (Thilo的建议,增加一个前导1将工作)。关于逗号:如果数字大小相等(01而不是1),那么我认为就不需要逗号了。
建议您查看每个字符提供6位信息的base64 -通常您的编码效率是log2(K)位每个符号,其中K是允许符号集中的符号数。
对于8位字符集,其中许多字符在URL中是不允许的,因此您需要选择一些合法URL字符的子集。
澄清一下:我的意思不是将"1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,0,4,0,0,0,0"字符串编码为base64——我的意思是找出您真正想要编码的信息,以原始二进制字节字符串表示,并将编码为base64。它将排除控制字符(尽管您可能希望使用一种替代形式,其中所有64个字符都可以在url中使用而无需转义),并且比将数字转换为可打印的数字形式更有效。
数字可以是0到99,每个数字之间用逗号隔开,用于String.split(",")检索String[]
好了,现在你有一个明确的定义了。我有个建议:
将您的信息从原始形式转换为二进制数/字节数组。如果您有的只是一串以逗号分隔的0-99之间的数字,那么这里有两个选项:
-
(慢)——将其视为基数为100的数字,转换为BigInteger(例如n = n * 100 + x[i]对于数组中的每个数字x),转换为字节数组,并确保在整个数组之前加上其长度,以便"0,0,0,0"可以区别于"0,0"(基数为100的数字相等,但长度不同)。然后将结果转换为base64
-
(更有效)——将其视为以128为基数的数字(因为这是2的幂),并使用100-127之间的任何数字作为终止字符。因此,每个6个数字块包含42(=6*7)位信息,这些信息可以使用base64编码为7个字符的字符串。(根据需要添加终止字符以达到原始数字的6的偶数倍)
由于输入可能是一个可变长度的数字数组,因此需要以某种方式对长度进行编码——要么直接作为前缀,要么通过使用终止字符间接地进行编码。
对于反向算法,只需反转步骤,您将获得一个从0到99的数字数组——使用前缀长度或终止字符来确定数组的大小——您可以将其转换为用逗号分隔的人类可读字符串。
如果您可以访问原始二进制形式的原始信息,然后将其编码为字符串,请使用它。(但请发布一个问题与输入格式要求的信息)
如果数字是0和255之间,你可以创建一个字节数组。一旦有了字节数组,就有了手动选择:
- 在字节数组上使用base64,这将创建一个紧凑的字符串(几乎)URL兼容
- 将它们转换为字符,使用您自己的基于最大值的算法
- 将它们转换为long,然后使用Long.toString(x,31)。
要转换回来,显然必须以相反的方式应用所选算法。
修改一种编码的程式:-
将二进制拆分为每组6位
制作64个字符的数组(选择允许的字符并保持ASCII顺序以便于搜索):- 0..9日,一. .Z, _, a…z ~
二进制和字符之间的映射