简体   繁体   English

如何生成超过2 ^ 30个组合的随机唯一字符串。 我也想扭转这个过程。 这可能吗?

[英]How to generate a random unique string with more than 2^30 combination. I also wanted to reverse the process. Is this possible?

I have a string which contains 3 elements: 我有一个包含3个元素的字符串:

  • a 3 digit code (example: SIN, ABD, SMS, etc) 3位代码(例如:SIN,ABD,SMS等)
  • a 1 digit code type (example: 1, 2, 3, etc) 1位数字代码类型(例如:1、2、3等)
  • a 3 digit number (example: 500, 123, 345) 3位数字(例如:500、123、345)

Example string: SIN1500, ABD2123, SMS3345, etc.. 字符串示例:SIN1500,ABD2123,SMS3345等。

I wanted to generate a UNIQUE 10 digit alphanumeric and case sensitive string (only 0-9/az/AZ is allowed), with more than 2^30 (about 1 billion) unique combination per string supplied. 我想生成一个唯一的10位字母数字和区分大小写的字符串(仅允许使用0-9 / az / AZ),每个字符串提供的唯一组合超过2 ^ 30(约10亿)。 The generated code must have a particular algorithm so that I can reverse the process. 生成的代码必须具有特定的算法,以便我可以逆转该过程。 For example: 例如:

public static void main(String[] args) {
    String test = "ABD2123";
    String result = generateData(test);
    System.out.println(generateOutput(test)); //for example, the output of this is: 1jS8g4GDn0
    System.out.println(generateOutput(result)); //the output of this will be ABD2123 (the original string supplied)
}

What I wanted to ask is is there any ideas/examples/libraries in java that can do this? 我想问的是,在Java中是否有任何想法/示例/库可以做到这一点? Or at least any hint on what keyword should I put on Google? 或至少我应该在Google上输入哪个关键字的任何提示?

I tried googling using the keyword java checksum, rng, security, random number, etc and also tried looking at some random number solution (java SecureRandom, xorshift RNG, java.util.zip's checksum, etc) but I can't seem to find one? 我尝试使用关键字Java校验和,RNG,安全性,随机数等进行谷歌搜索,还尝试查看一些随机数解决方案(Java SecureRandom,xorshift RNG,java.util.zip的校验和等),但我似乎找不到一?

Thanks! 谢谢!

EDIT: 编辑:

My use case for this program is to generate some kind of unique voucher number to be used by specific customers. 我对该程序的用例是生成某种独特的凭单号,以供特定客户使用。

The string supplied will contains 3 digit code for company ID, 1 digit code for voucher type, and a 3 digit number for the voucher nominal. 提供的字符串将包含用于公司ID的3位数字代码,用于凭证类型的1位数字代码和用于凭证名义的3位数字。 I also tried adding 3 random alphanumeric (so the final digit is 7 + 3 digit = 10 digit). 我还尝试添加3个随机字母数字(因此最后一位是7 + 3位= 10位)。 This is what I've done so far, but the result is not very good (only about 100 thousand combination): 到目前为止,这是我所做的,但是效果不是很好(仅约10万种组合):

public static String in ="somerandomstrings";
public static String out="someotherrandomstrings";

public static String encrypt(String kata)
throws Exception {
    String result="";
    String ina=in;
    String outa=out;
    Random ran = new Random();
    Integer modulus=in.length();
    Integer offset= ((Integer.parseInt(Utils.convertDateToString(new Date(), "SS")))+ran.nextInt(60))/2%modulus;

    result=ina.substring(offset, offset+1);
    ina=ina+ina;
    ina=ina.substring(offset, offset+modulus);
    result=result+translate(kata, ina, outa);

    return result;
}

EDIT: 编辑:

I'm sorry I forgot to put the "translate" function : 对不起,我忘了添加“翻译”功能:

    public static String translate(String kata,String  seq1, String  seq2){
    String result="";
    if(kata!=null&seq1!=null&seq2!=null){
        String[] a=kata.split("");
        for (int j = 1; j < a.length; j++) {
            String b=a[j];                      
            String[]seq1split=seq1.split("");
            String[]seq2split=seq2.split("");
            int hint=seq1.indexOf(b)+1;

            String sq="";
            if(seq1split.length>hint)
                sq=seq1split[hint];
            String sq1="";
            if(seq2split.length>hint)
                sq1=seq2split[hint];

            b=b.replace(sq, sq1);

            result=result+b;
        }
    }
    return result;
}

EDIT: 编辑:

Okay, after a few days I'm currently struggling to convert the Ruby code provided by @sarnold, this is the code I've come up with : 好的,几天后,我目前正在努力转换@sarnold提供的Ruby代码,这是我想出的代码:

public class Test {

static char START_A = "A".charAt(0);
static char START_a = "a".charAt(0);
static char START_0 = "0".charAt(0);
static int CODEMASK = ((1 << 28) - 1); //turn on lower 28 bits
static int RANDOMMASK = ((1 << 60) - 1) & ~ CODEMASK; //turn on upper 32 bits

public static void main(String[] args) {

    String[] test = new String[]{
            //"AAA0000", "SIN1500", "ABD2123", "SMS3345", "ZZZ9999",
            //"ABD2123", "ABD2123", "ABD2123", "ABD2123", "ABD2123"
            "ABD2123"
            };

    for(String t : test){
        long c = compress(t);
        long a = add_random(c);
        String output = to_output(a);
        long input = from_output(output);

        String[] new_c_r = remove_random(input);
        String u = uncompress(Long.valueOf(new_c_r[0]));

        System.out.println("Original input: " + t);
        System.out.println("    compressed: " + c);
        System.out.println("    after adding random amount: " + a);
        System.out.println("    *output: " + output);
        System.out.println("    *input: " + input);
        System.out.println("    random amount added: " + new_c_r[1]);
        System.out.println("    after removing random amount: " + new_c_r[0]);
        System.out.println("    uncompressed: " + u);
        System.out.println("-----------------------------------------------------------------");
    }

}

public static long compress(String line){ //7 character
    char a = line.charAt(0);
    char b = line.charAt(1);
    char c = line.charAt(2);
    char h = line.charAt(3);
    char i = line.charAt(4);
    char j = line.charAt(5);
    char k = line.charAt(6);

    long small_a = (long) a - START_A;
    long small_b = (long) b - START_A;
    long small_c = (long) c - START_A;
    long letters = (small_a * 26 * 26) + (small_b * 26) + small_c;
    long numbers = letters * 10000 + h * 1000 + i*100 + j*10 + k;
    return numbers;
}

public static String uncompress(long number){
    long k = number % 10;
    number /= 10;
    long j = number % 10;
    number /= 10;
    long i = number % 10;
    number /= 10;
    long h = number % 10;
    number /= 10;
    long small_c = number % 26;
    number /= 26;
    long small_b = number % 26;
    number /= 26;
    long small_a = number % 26;
    number /= 26;

    if (number != 0) throw new RuntimeException("input wasn't generated with compress()");

    long a = small_a + START_A;
    long b = small_b + START_A;
    long c = small_c + START_A;

    StringBuffer result = new StringBuffer();
    result.append((char) a).append((char) b).append((char) c).append(h).append(i).append(j).append(k);

    return result.toString();
}

public static long add_random(long number){
    return (((long) (Math.random()* Math.pow(2, 31))) << 28) + number;
}

public static String[] remove_random(long number){
    return new String[]{String.valueOf(number & CODEMASK), String.valueOf(number & RANDOMMASK)};
}

public static String to_output(long number){
    List<Character> a = new ArrayList<Character>();
    do{
        a.add(transform_out(number % 62));
        number /= 62;
    }while(number > 0);

    Collections.reverse(a);

    StringBuffer result = new StringBuffer();
    for(int i=0; i<a.size(); i++){
        Character s = (Character) a.get(i);
        result.append(s);
    }

    return result.toString();
}

public static long from_output(String string){
    long num = 0;
    for(char c : string.toCharArray()){
        num *= 62;
        num += transform_in(c);
    }
    return num;
}

public static char transform_out(long small){
    long out;

    if (small < 0 || small > 61){
        throw new RuntimeException("small should be between 0 and 61, inclusive");
    }
    if(small < 26){
        out = START_A + small;
    }else if(small < 52){
        out = START_a + (small-26);
    }else{
        out = START_0 + (small-52);
    }
    return (char) out;
}


public static long transform_in(char c){
    if(!String.valueOf(c).matches("[a-zA-Z0-9]")){ 
        throw new RuntimeException("char should be A-Z, a-z, or 0-9, inclusive");
    }
    long num = (long) c;

    long out;
    if(num >= START_A && num <= START_A+26) out = num-START_A;
    else if(num >= START_a && num <= START_a+26) out = (num-START_a) + 26;
    else if(num >= START_0 && num <= START_0+10) out = (num-START_0) + 52;
    else throw new RuntimeException("Salah, bego!");

    return out;
}}

but I can't seem to make this code right, the result is like this : 但我似乎无法正确编写此代码,结果如下所示:

Original input: ABD2123
compressed: 345451
after adding random amount: 62781252268541291
*output: EnhZJdRFaj
*input: 62781252268541291
random amount added: 0
after removing random amount: 345451
uncompressed: ABI5451

as you've probably noticed the "compressed" and the "uncompressed" field didn't show the same amount. 您可能已经注意到“压缩”字段和“未压缩”字段显示的金额不同。 The "random amount added" field is also always returning 0 value. “添加的随机数量”字段也始终返回0值。 Is there anyone who can help see where I'm wrong in this code? 有没有人可以帮助您了解此代码中的错误之处?

I'm sorry if it's a mistake to put this code in this thread I will create another thread. 很抱歉,如果将此代码放入此线程是错误的,我将创建另一个线程。

Thank You, Yusuf 谢谢你,优素福

Your requirements are very unclear. 您的要求非常不清楚。 So I'm going to restrict my answer to generalities: 因此,我将回答限于一般性:

There are functions called cryptographic hash functions that will map from an arbitrary sequence of bytes to a "hash", with the property that the probability of any two related inputs giving the same output is vanishingly small. 有一些称为密码哈希函数的函数,它将从任意字节序列映射到一个“哈希”,其特性是任何两个相关输入给出相同输出的概率都非常小。 However, cryptographic hash functions are (by design) not reversible ... by design. 但是,密码哈希函数(根据设计)是不可逆的……根据设计。

A mapping from one "space" of Strings to another that is reversible, can be implemented in two ways: 从字符串的一个“空间”到另一个可逆的映射可以两种方式实现:

  • You can generate arbitrary Strings as the mapped Strings, and use data structures such as a hash tables to store the forward and reverse mappings. 您可以生成任意字符串作为映射的字符串,并使用数据结构(例如哈希表)存储正向和反向映射。

  • You can use a cryptographic hash function to generate the mapped Strings, and use data structures such as a hash tables to store the reverse mappings. 您可以使用加密哈希函数生成映射的字符串,并使用数据结构(例如哈希表)存储反向映射。

  • You can use a reversible function to transform between the original and mapped Strings. 您可以使用可逆函数在原始字符串和映射的字符串之间进行转换。 This has the problem that the mapping is likely to be be easy to reverse engineer. 这具有映射可能易于逆向工程的问题。

An example of the latter might be to turn the original String into bytes and then base64 encode it. 后者的一个示例可能是将原始String转换为字节,然后进行base64编码。 Or even more trivially, you could insert a random character between each character in the input string. 甚至更简单的说,您可以在输入字符串的每个字符之间插入一个随机字符。 (Obviously, transformations like these can be reverse engineered in a few minutes ... given enough examples. So one has to doubt the wisdom of this approach.) (显然,此类转换可以在几分钟内进行逆向工程……给出足够的示例。因此,人们不得不怀疑这种方法的智慧。)

Without better requirements, it is unclear which (if any) of these approaches is what you need. 如果没有更好的要求,则不清楚您需要哪种方法(如果有)。

I've written a Ruby tool that does what you need. 我已经编写了可以满足您需求的Ruby工具。 (Sorry it isn't Java, but my Java is over a decade old by now. But the general idea should port to Java without hassle.) (很抱歉,它不是Java,但是我的Java已有十多年的历史了。但是一般的想法应该毫不费力地移植到Java。)

In short, the given input string (7 bytes) is compressed into a number between 0 and 175_760_000 (28 bits). 简而言之,将给定的输入字符串(7个字节)压缩为0175_760_000 (28位)之间的数字。 A 32 bit random number is prepended, making a 60 bit integer, which is encoded into a 10-character output string. 前面有一个32位随机数,是一个60位整数,它被编码成10个字符的输出字符串。

My earlier math was wrong; 我以前的数学是错误的。 since your input is only 28 bits long and your desired output is 60 bits long, that leaves 32 bits for adding roughly two billion random permutations. 因为您的输入只有28位长,而您希望的输出是60位长,所以剩下32位用于添加大约20亿个随机排列。 I mistyped when performing my calculations. 在执行计算时输入错误。

#!/usr/bin/ruby -w
START_A = "A"[0]
START_a = "a"[0]
START_0 = "0"[0]
CODEMASK = ((1 << 28) - 1) # turn on lower 28 bits
RANDOMMASK = ((1 << 60) - 1) & ~ CODEMASK # turn on upper 32 bits


def compress(line)
    a, b, c, h, i, j, k = line.chomp().each_char().entries()
    a, b, c = a[0], b[0], c[0]
    small_a, small_b, small_c = a - START_A, b - START_A, c - START_A
    letters = (small_a * 26**2) + (small_b * 26) + small_c
    h, i, j, k = Integer(h), Integer(i), Integer(j), Integer(k)
    number = letters * 10_000 + h*1000 + i*100 + j*10 + k
end

def uncompress(number)
    k = number % 10
    number /= 10
    j = number % 10
    number /= 10
    i = number % 10
    number /= 10
    h = number % 10
    number /= 10
    small_c = number % 26
    number /= 26
    small_b = number % 26
    number /= 26
    small_a = number % 26
    number /= 26
    if (number != 0)
            raise "input wasn't generated with compress()"
    end
    a, b, c = small_a + START_A, small_b + START_A, small_c + START_A
    [a.chr(), b.chr(), c.chr(), h.to_s(), i.to_s(), j.to_s(), k.to_s()].join()
end

def add_random(number)
    (rand(2**31) << 28) + number
end 

def remove_random(number)
    [number & CODEMASK, number & RANDOMMASK]
end

def to_output(number)
    a = []
    begin
            a << transform_out(number % 62)
            number /= 62
    end while(number > 0)
    a.reverse().join()
end

def from_output(string)
    num = 0
    string.each_char() do |c|
            num *= 62
            num += transform_in(c)
    end     
    num
end

def transform_out(small)
    if (small < 0 || small > 61)
            raise "small should be between 0 and 61, inclusive"
    end
    if (small < 26)
            out = START_A+small
    elsif (small < 52)
            out = START_a+(small-26)
    else
            out = START_0+(small-52)
    end
    out.chr()
end

def transform_in(char)
    if (/^[A-Za-z0-9]$/ !~ char)
            raise "char should be A-Z, a-z, or 0-9, inclusive"
    end
    num = char[0]
    out = case num
    when START_A .. START_A+26 then num - START_A
    when START_a .. START_a+26 then (num - START_a) + 26
    when START_0 .. START_0+10 then (num - START_0) + 52
    end

    out
end


begin
    while(line = DATA.readline()) do
            line.chomp!()
            c = compress(line)
            a = add_random(c)
            output = to_output(a)
            input = from_output(output)
            new_c, r = remove_random(input)
            u = uncompress(new_c)

            printf("original input: %s\n compressed: %d\n after adding random amount: %d\n *output: %s\n *input: %s\n random amount added: %d\n after removing random amount: %d\nuncompressed: %s\n", line, c, a, output, input, r, new_c, u)
    end
rescue EOFError => e
end


__END__
AAA0000
SIN1500
ABD2123
SMS3345
ZZZ9999

Running the program with the five inputs given at the bottom results in this output: 使用底部给出的五个输入来运行程序,结果如下:

$ ./compress.rb
original input: AAA0000
 compressed: 0
 after adding random amount: 508360097408221184
 *output: liSQkzXL1G
 *input: 508360097408221184
 random amount added: 508360097408221184
 after removing random amount: 0
uncompressed: AAA0000
original input: SIN1500
 compressed: 123891500
 after adding random amount: 421470683267231532
 *output: fIVFtX9at2
 *input: 421470683267231532
 random amount added: 421470683143340032
 after removing random amount: 123891500
uncompressed: SIN1500
original input: ABD2123
 compressed: 292123
 after adding random amount: 414507907112269083
 *output: emb6JfDhUH
 *input: 414507907112269083
 random amount added: 414507907111976960
 after removing random amount: 292123
uncompressed: ABD2123
original input: SMS3345
 compressed: 124983345
 after adding random amount: 383242064398325809
 *output: cTPpccLAvn
 *input: 383242064398325809
 random amount added: 383242064273342464
 after removing random amount: 124983345
uncompressed: SMS3345
original input: ZZZ9999
 compressed: 175759999
 after adding random amount: 27149937199932031
 *output: CAVf14tiRh
 *input: 27149937199932031
 random amount added: 27149937024172032
 after removing random amount: 175759999
uncompressed: ZZZ9999

The lines you're really looking for are original input , *output , and uncompressed . 您真正要查找的行是original input*outputuncompressed Your client has the original input lines, after using the to_output(add_random(compress(input))) you will get the ten-character A-Za-z0-9 output in the *output line. 您的客户端具有original input行,使用to_output(add_random(compress(input)))您将在*output行中获得10个字符的A-Za-z0-9输出。 You hand that to users, and it's a magic token of some sort. 您将其交给用户,这是某种魔术令牌。 Then when it is time to validate them, you use remove_random(from_output(user_string)) to discover both the random value added to the string and an integer that you can hand to uncompress() . 然后,当需要验证它们时,可以使用remove_random(from_output(user_string))发现添加到字符串的随机值可以传递给uncompress()的整数。

One very important note : The input AAA0000 is stored in plaintext in the lower 28 bits. 一个非常重要的说明 :输入AAA0000以明文形式存储在低28位中。 The random number is stored in plaintext in the upper 32 bits. 随机数以明文形式存储在高32位中。 This is simply an obfuscation of the original inputs, it wouldn't be hard for someone to discover the pattern if they have two inputs and outputs. 这只是对原始输入的混淆 ,如果有人有两个输入和输出,那么发现某人的模式就很困难。 Heck, they might even correctly guess the algorithm given only a single input and output. 哎呀,他们甚至可以正确地猜测仅给定单个输入和输出的算法。

If you need this to be cryptographically strong, then you've still got some work ahead of you :) but that might be as easy as XORing the intermediate 60 bit number with rc4 output of the username or something like that. 如果您需要加密功能强大,那么您仍然需要做一些工作:),但这可能与将中间60位数字与用户名的rc4输出进行XOR或类似操作一样容易。

Short Explanation 简短说明

Your input strings can be interpreted as integers, but with a twist : the first three 'digits' are in base 26 , the next four digits are in base 10. The number will be less than 175_760_000 . 您的输入字符串可以被解释为整数,但与一捻 :前三个“数字”是在基座26 ,接下来的四位是在基座10的数量将小于175_760_000 Uniquely storing numbers between 0 and 175_760_000 requires 28 bits. 唯一存储0175_760_000之间的数字需要28位。 Your output strings are also a number, ten digits long, with each 'digit' in base 62. (Think base64 but without - , / , or = (for padding).) 62 possible values and ten positions gives you a maximum value of 839_299_365_868_340_224 , which can be represented in 60 bits. 您的输出字符串也是一个数字,长度为十位数,每个“位数”都以62为底。(请考虑使用base64,但不带-/= (用于填充)。)62个可能的值和十个位置为您提供最大值839_299_365_868_340_224 ,可以60位表示。

The input string only takes 28 bits to represent, your output string has 60 bits available, and that leaves 32 bits available for storing a randomly-generated number. 输入字符串仅用28位表示,输出字符串有60位可用,剩下32位可用于存储随机生成的数字。 If we multiply a 32 -bit number by 2^28 (the same as a left-shift by 28: 1 << 28 ) then the lower 28 bits will be free for the originally input number. 如果将32位数字乘以2^28 (与左移乘以28: 1 << 28 ),则低28位将对原始输入数字可用。

Once we've calculated the 60 bit number, we output it in our base 62 notation for human consumption. 一旦计算出60位数字,就将其输出到以62为基础的符号中,以供人类使用。

To reverse the process, you decode the base 62 number to get our 60 bit number; 为了逆转这一过程,您可以解码基数为62的数字以获得我们的60位数字。 split the 60 bit number into the lower 28 bit input number and upper 32 bit random number, and then output the 28 bit number in the original format: three base 26 digits followed by four base 10 digits. 60位数字分为低28位输入数字和高32位随机数字,然后以原始格式输出28位数字:三位26位基本数字,然后四位10位基本数字。

FINAL UPDATE 最后更新

Yusuf, excellent work converting my Ruby to Java. Yusuf,将我的Ruby转换为Java的出色工作。 I'm very impressed, especially given how good your Java version looks: your version is more legible. 我印象深刻,尤其是考虑到您的Java版本看起来不错 :您的版本更易读。 I'm jealous and impressed. 我嫉妒和感动。 :) :)

I found the two small bugs that remained in your program: RANDOMMASK was accidentally initialized to 0 , because Java doesn't promote all operands into the final data type when executing arithmetic shift statements. 我发现程序中仍然存在两个小错误: RANDOMMASK意外地初始化为0 ,因为在执行算术移位语句时Java 不会将所有操作数提升为最终数据类型 Changing 1 to 1L forces the result of 1L << 60 to be a long ; 改变11L力的结果1L << 60是一个long ; without the L , the result of 1 << 60 is an int , and isn't large enough to hold the full number. 如果没有L ,则1 << 60的结果是一个int ,并且不足以容纳整数。

Further, the digits weren't being compressed correctly; 此外,数字未正确压缩。 my Ruby code parsed the characters as an integer, and your Java code interpreted the characters as an integer. 我的Ruby代码将字符解析为整数,而Java代码将字符解析为整数。 (Yours used the character's value; mine converted the character to an integer based on the ascii meaning of the character. Mine wasn't really parsing , since it is just doing a subtraction, but if it were a string, String.toInteger(character) would do the same thing, so it is a lot like parsing.) (您使用了字符的值;我根据字符的ascii 含义将字符转换为整数。Mine并不是真正的解析器 ,因为它只是在进行减法运算,但是如果是字符串,则为String.toInteger(character)会做同样的事情,所以很像解析。)

But your uncompress logic was correct, and because of the mismatch, the output was incorrect. 但是您的解压缩逻辑是正确的,并且由于不匹配,输出不正确。 So I changed your code to parse the digits into integers (and changed from char to int to silence a pointless warning). 因此,我更改了代码以将数字解析为整数(并从char更改为int以使无意义的警告消失)。

Here's a diff of what I had to change in your program to make it work: 为了使它正常工作,我在您的程序中做了一些更改:

--- Compress.java.orig  2011-03-25 16:57:47.000000000 -0700
+++ Compress.java   2011-03-25 17:09:42.000000000 -0700
@@ -1,12 +1,12 @@
-import java.util.*
+import java.util.*;

 public class Compress {

 static char START_A = "A".charAt(0);
 static char START_a = "a".charAt(0);
 static char START_0 = "0".charAt(0);
-static int CODEMASK = ((1 << 28) - 1); //turn on lower 28 bits
-static int RANDOMMASK = ((1 << 60) - 1) & ~ CODEMASK; //turn on upper 32 bits
+static long CODEMASK = ((1 << 28) - 1); //turn on lower 28 bits
+static long RANDOMMASK = ((1L << 60) - 1) & ~ CODEMASK; //turn on upper 32 bits

 public static void main(String[] args) {

@@ -42,10 +42,10 @@
     char a = line.charAt(0);
     char b = line.charAt(1);
     char c = line.charAt(2);
-    char h = line.charAt(3);
-    char i = line.charAt(4);
-    char j = line.charAt(5);
-    char k = line.charAt(6);
+    int h = line.charAt(3) - START_0;
+    int i = line.charAt(4) - START_0;
+    int j = line.charAt(5) - START_0;
+    int k = line.charAt(6) - START_0;

     long small_a = (long) a - START_A;
     long small_b = (long) b - START_A;

And now the full source, just in case that's easier :) 现在是完整的源代码,以防万一,这很容易:)

import java.util.*;

public class Compress {

static char START_A = "A".charAt(0);
static char START_a = "a".charAt(0);
static char START_0 = "0".charAt(0);
static long CODEMASK = ((1 << 28) - 1); //turn on lower 28 bits
static long RANDOMMASK = ((1L << 60) - 1) & ~ CODEMASK; //turn on upper 32 bits

public static void main(String[] args) {

    String[] test = new String[]{
            //"AAA0000", "SIN1500", "ABD2123", "SMS3345", "ZZZ9999",
            //"ABD2123", "ABD2123", "ABD2123", "ABD2123", "ABD2123"
            "ABD2123"
            };

    for(String t : test){
        long c = compress(t);
        long a = add_random(c);
        String output = to_output(a);
        long input = from_output(output);

        String[] new_c_r = remove_random(input);
        String u = uncompress(Long.valueOf(new_c_r[0]));

        System.out.println("Original input: " + t);
        System.out.println("    compressed: " + c);
        System.out.println("    after adding random amount: " + a);
        System.out.println("    *output: " + output);
        System.out.println("    *input: " + input);
        System.out.println("    random amount added: " + new_c_r[1]);
        System.out.println("    after removing random amount: " + new_c_r[0]);
        System.out.println("    uncompressed: " + u);
        System.out.println("-----------------------------------------------------------------");
    }

}

public static long compress(String line){ //7 character
    char a = line.charAt(0);
    char b = line.charAt(1);
    char c = line.charAt(2);
    int h = line.charAt(3) - START_0;
    int i = line.charAt(4) - START_0;
    int j = line.charAt(5) - START_0;
    int k = line.charAt(6) - START_0;

    long small_a = (long) a - START_A;
    long small_b = (long) b - START_A;
    long small_c = (long) c - START_A;
    long letters = (small_a * 26 * 26) + (small_b * 26) + small_c;
    long numbers = letters * 10000 + h * 1000 + i*100 + j*10 + k;
    return numbers;
}

public static String uncompress(long number){
    long k = number % 10;
    number /= 10;
    long j = number % 10;
    number /= 10;
    long i = number % 10;
    number /= 10;
    long h = number % 10;
    number /= 10;
    long small_c = number % 26;
    number /= 26;
    long small_b = number % 26;
    number /= 26;
    long small_a = number % 26;
    number /= 26;

    if (number != 0) throw new RuntimeException("input wasn't generated with compress()");

    long a = small_a + START_A;
    long b = small_b + START_A;
    long c = small_c + START_A;

    StringBuffer result = new StringBuffer();
    result.append((char) a).append((char) b).append((char) c).append(h).append(i).append(j).append(k);

    return result.toString();
}

public static long add_random(long number){
    return (((long) (Math.random()* Math.pow(2, 31))) << 28) + number;
}

public static String[] remove_random(long number){
    return new String[]{String.valueOf(number & CODEMASK), String.valueOf(number & RANDOMMASK)};
}

public static String to_output(long number){
    List<Character> a = new ArrayList<Character>();
    do{
        a.add(transform_out(number % 62));
        number /= 62;
    }while(number > 0);

    Collections.reverse(a);

    StringBuffer result = new StringBuffer();
    for(int i=0; i<a.size(); i++){
        Character s = (Character) a.get(i);
        result.append(s);
    }

    return result.toString();
}

public static long from_output(String string){
    long num = 0;
    for(char c : string.toCharArray()){
        num *= 62;
        num += transform_in(c);
    }
    return num;
}

public static char transform_out(long small){
    long out;

    if (small < 0 || small > 61){
        throw new RuntimeException("small should be between 0 and 61, inclusive");
    }
    if(small < 26){
        out = START_A + small;
    }else if(small < 52){
        out = START_a + (small-26);
    }else{
        out = START_0 + (small-52);
    }
    return (char) out;
}


public static long transform_in(char c){
    if(!String.valueOf(c).matches("[a-zA-Z0-9]")){ 
        throw new RuntimeException("char should be A-Z, a-z, or 0-9, inclusive");
    }
    long num = (long) c;

    long out;
    if(num >= START_A && num <= START_A+26) out = num-START_A;
    else if(num >= START_a && num <= START_a+26) out = (num-START_a) + 26;
    else if(num >= START_0 && num <= START_0+10) out = (num-START_0) + 52;
    else throw new RuntimeException("Salah, bego!");

    return out;
}}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM