【问题标题】:Simple java algorithm to encode/decode the following string用于编码/解码以下字符串的简单 java 算法
【发布时间】:2011-08-01 00:40:24
【问题描述】:

假设我有
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 解码为逗号分隔的数字。

【问题讨论】:

  • 请描述输入格式要求(例如范围为 0-??? 的整数)
  • 您真的需要将其限制为罗马字符吗?因为如果数字等没问题,就有 Java 版本的 UUENCODE 算法。
  • @QuentinUK 看到我的编辑。只要字符能被浏览器URL地址框接受就可以了。

标签: java compression decode encode


【解决方案1】:

这与 Nathan Hughes 的解决方案相比并没有太大的改进,但字符串越长,您获得的节省就越多。

编码:创建一个以“1”开头的String,使源字符串中的每个数字为2位,因此“0”变为“00”,“5”变为“05”,“99”变为“99”等。以 36 为基数表示结果数。

解码:取base 36的数字/字符串,改回base 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、带有“”和“-”的为 64。BigInteger.toString() 方法最多只占用 Character.MAX_RADIX,即 36(无大写字母)。如果你能想办法把一个大数改成base 64,那么得到的编码String会更短。

编辑:看起来这是为你做的:http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html

【讨论】:

    【解决方案2】:

    将其保存为 36 进制数怎么样?

    在 Java 中是这样的

    new java.math.BigInteger("120000000000000000012230400403").toString(36)
    

    这将评估为"bmtwva1131gpefvb1xv"

    你会得到原来的号码与

    new java.math.BigInteger("bmtwva1131gpefvb1xv", 36)
    

    这是一个很好的点,这不处理前导 0(Thilo 建议添加前导 1 会起作用)。关于逗号:如果数字大小相同(01 而不是 1),那么我认为不需要逗号。

    【讨论】:

    • 我没有给你-1,但它可能是为了使用clojure。不过,这是个好主意(考虑到问题缺乏细节,对输入数据进行了合理的假设)。 +1 如果你用 Java 语法给出它。
    • 前导零可能是个问题。可能需要以一个额外的“1”开头来解决这个问题。
    • 逗号不能被删除,它解码编码的字符串应该带回逗号
    • @Qin:好的,我同意 Jason S 我们需要更多关于输入格式的细节。
    • hmm,我如何从“bmtwva1131gpefvb1xv”取回号码??
    【解决方案3】:

    建议您查看base64,它为每个字符提供 6 位信息——通常您的编码效率是 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,1,2,2,3,0,4,0,0,0,4,0,3" string as base64 - 我的意思是找出你真正想要编码的信息,表示为原始字符串二进制字节,并在 base64 中编码 that。它将排除控制字符(尽管您可能希望使用alternate form,其中所有 64 个字符都可以在 URL 中使用而无需转义)并且比将数字转换为可打印的数字形式更有效。


    数字可以从0到99,每个数字用逗号隔开,以便String.split(",")检索String[]

    好的,现在您有了明确的定义。这是一个建议:

    将您的信息从其原始形式转换为二进制数/字节数组。如果您只有一串 0-99 之间的逗号分隔数字,那么这里有两个选项:

    • (slow) -- 视为以 100 为底的数字,转换为 BigInteger(例如,对于数组中的每个数字 x,n = n * 100 + x[i]),转换为字节数组,然后一定要在整个事物之前加上它的长度,以便“0,0,0,0”可以与“0,0”区分开来(数字等于100,但它有不同的长度。然后将结果转换为base64 .

    • (更有效)- 视为以 128 为底的数字(因为这是 2 的幂),并使用 100-127 之间的任何数字作为终止字符。因此,每个 6 个数字块包含 42 (=6*7) 位信息,可以使用 base64 将其编码为 7 个字符的字符串。 (根据需要使用终止字符填充,以达到原始数字的 6 的偶数倍。)

    因为您有一个可能变长的数字数组作为输入,所以您需要以某种方式对长度进行编码 - 直接作为前缀,或间接使用终止字符。

    对于逆算法,只需反转步骤,您将获得一个从 0 到 99 的数字数组 - 使用前缀长度或终止字符来确定数组的大小 - 您可以将其转换为用逗号分隔的可读字符串。

    如果您可以在原始二进制形式的原始信息被编码为字符串之前访问它,请改用它。 (但请发布有关该信息输入格式要求的问题)

    【讨论】:

    • 战神!沉默的-1er又来了!
    • "我想把它编码成一个字符更少的字符串" 这个问题对输入数据的细节有点简短,但是一般数据的base64使得输出比输入长(6位得到变成了 8)。
    • -1 抱怨投反对票。我的意思是什么?给出理由可能是一种很好的礼仪,但并非意料之中。
    • @phooji:他不是在抱怨投反对票,而是在抱怨投反对票的人没有在评论中解释原因。这对我来说似乎很文明。当我对答案有疑问时,我会先发表评论而不投反对票。
    • @Jason S:根据反映更新后问题的更新内容,我对您的答案投了反对票。
    【解决方案4】:

    如果数字在 0 到 255 之间,您可以从中创建一个字节数组。一旦你有了一个字节数组,你就有了手动选择:

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

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

    【讨论】:

      【解决方案5】:

      修改后的 UUENCODE:-

      将二进制分成 6 位组

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

      二进制和字符之间的映射。

      【讨论】:

        猜你喜欢
        • 2013-07-06
        • 1970-01-01
        • 2016-06-17
        • 1970-01-01
        • 2021-04-19
        • 2023-03-08
        • 1970-01-01
        • 2017-06-14
        • 2012-10-24
        相关资源
        最近更新 更多