简单的java算法编码/解码以下字符串



假设我有
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之间,你可以创建一个字节数组。一旦有了字节数组,就有了手动选择:

  1. 在字节数组上使用base64,这将创建一个紧凑的字符串(几乎)URL兼容
  2. 将它们转换为字符,使用您自己的基于最大值的算法
  3. 将它们转换为long,然后使用Long.toString(x,31)。

要转换回来,显然必须以相反的方式应用所选算法。

修改一种编码的程式:-

将二进制拆分为每组6位

制作64个字符的数组(选择允许的字符并保持ASCII顺序以便于搜索):- 0..9日,一. .Z, _, a…z ~

二进制和字符之间的映射

最新更新