问题描述
我有一个包含3个元素的字符串:
- 一个3位数代码(例如:SIN,ABD,SMS等)
- 1位数代码类型(例如:1,2,3等)
- 一个3位数字(例如:500,123,345)
示例字符串:SIN1500,ABD2123,SMS3345等。
我想生成一个独特的10位字母数字和区分大小写的字符串(只允许0-9 / az / AZ),每个字符串提供超过2 ^ 30(约10亿)的唯一组合。生成的代码必须具有特定的算法,以便我可以反转该过程。例如:
public static void main(String [] args){
String test =ABD2123;
String result = generateData(test);
System.out.println(generateOutput(test)); //例如,输出为:1jS8g4GDn0
System.out.println(generateOutput(result)); //这个输出将是ABD2123(提供的原始字符串)
}
什么我想问一下,java中是否有任何想法/示例/库可以做到这一点?或者至少我应该在Google上放置什么关键字?
我尝试使用关键字java checksum,rng,security,random number等进行谷歌搜索,并尝试查看一些随机数解决方案(java SecureRandom,xorshift RNG,java。 util.zip的校验和等)但我似乎找不到一个?
谢谢!
编辑:
此程序的用例是生成某种特定客户使用的唯一凭证编号。
提供的字符串将包含公司ID的3位数代码,凭证类型的1位数代码和名义凭证的3位数字。我还尝试添加3个随机字母数字(所以最后的数字是7 + 3位= 10位)。这是我到目前为止所做的,但结果并不是很好(只有大约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();
整数模数= in.length();
整数偏移=((Integer.parseInt(Utils.convertDateToString(new Date(),SS)))+ ran.nextInt(60))/ 2%模数;
result = ina.substring(offset,offset + 1);
ina = ina + ina;
ina = ina.substring(offset,offset + modulus);
result = result + translate(kata,ina,outa);
返回结果;
}
编辑:
对不起,我忘记了翻译功能:
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>提示)
sq = seq1split [hint];
String sq1 =;
if(seq2split.length>提示)
sq1 = seq2split [hint];
b = b.replace(sq,sq1);
结果=结果+ b;
}
}
返回结果;
}
编辑:
好的,几天后我正在努力转换@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 static int RANDOMMASK =((1<< 60)-1)& ~CODEMASK; //打开高位32位
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);
字符串u = uncompress(Long.valueOf(new_c_r [0]));
System.out.println(原始输入:+ t);
System.out.println(compressed:+ c);
System.out.println(添加随机数量后:+ a);
System.out.println(* output:+ output);
System.out.println(* input:+ input);
System.out.println(随机数量增加:+ new_c_r [1]);
System.out.println(删除随机数量后:+ new_c_r [0]);
System.out.println(未压缩:+ u);
System.out.println(--------------------------------------- --------------------------);
}
}
public static long compress(String line){// 7个字符
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 =(长)a - START_A;
long small_b =(long)b - START_A;
long small_c =(长)c - START_A;
长字母=(small_a * 26 * 26)+(small_b * 26)+ small_c;
长数字=字母* 10000 + h * 1000 + i * 100 + j * 10 + k;
返回数字;
}
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)抛出新的RuntimeException(输入不是用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);
返回result.toString();
}
public static long add_random(long number){
return(((long)(Math.random()* Math.pow(2,31)))< ;< 28)+数字;
}
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);
}
返回result.toString();
}
public static long from_output(String string){
long num = 0;
for(char c:string.toCharArray()){
num * = 62;
num + = transform_in(c);
}
返回num;
}
public static char transform_out(long small){
long out;
if(small< 0 || small> 61){
抛出新的RuntimeException(small应介于0和61之间,包括);
}
if(小于< 26){
out = START_A + small;
}否则if(小于< 52){
out = START_a +(small-26);
}其他{
out = START_0 +(小-52);
}
返回(char)out;
}
public static long transform_in(char c){
if(!String.valueOf(c).matches([a-zA-Z0- 9])){
抛出新的RuntimeException(char应该是AZ,az或0-9,包括);
}
long num =(long)c;
多头;
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抛出新的RuntimeException(Salah,bego!);
退出;
}}
但我似乎无法使这段代码正确,结果是像这样:
原始输入:ABD2123
压缩:345451
加入随机数量后:62781252268541291
*输出:EnhZJdRFaj
*输入:62781252268541291
随机数量增加:0
移除随机数量后:345451
未压缩:ABI5451
因为你可能已经注意到压缩和未压缩字段没有显示相同的数量。 随机添加量字段也始终返回0值。
是否有人可以帮助查看我在这段代码中出错的地方?
如果把这段代码放在这个帖子中是个错误,我很抱歉我将创建另一个帖子。
谢谢你,
Yusuf
我编写了一个可以满足您需求的Ruby工具。 (对不起,它不是Java,但我的Java已经有十多年的历史了。但是一般的想法应该毫不费力地移植到Java。)
简而言之,给定的输入字符串(7个字节)被压缩为 0
和 175_760_000
(28位)之间的数字。前置一个32位随机数,产生一个60位整数,编码成一个10个字符的输出字符串。
我之前的数学错误;因为你的输入只有28位长,你想要的输出是60位长,所以留下32位用于增加大约20亿个随机排列。我在执行计算时输入错误。
#!/ usr / bin / ruby -w
START_A =A [0]
START_a =a[0]
START_0 =0[0]
CODEMASK =((1<< 28) - 1)#turn on lower 28比特
RANDOMMASK =((1
def压缩(线)
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 =整数(h),整数(i),整数(j),整数(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输入未生成压缩()
end
a,b,c = small_a + START_A,small_b + START_A,小l_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)+数字
结束
def remove_random(数字)
[数字& CODEMASK,数字& 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(小< 0 || small> 61)
加小应该在0到61之间,包括
结束
if(small out = START_A +小
elsif(小< 52)
out = START_a +(小-26)
其他
out = START_0 +(小-52)
结束
out.chr()
end
def transform_in(char)
if(/ ^ [A-Za-z0-9] $ /!~char)
提高char应为AZ,az或0-9,包含
结束
num = char [0]
out = case num
START_A时。 .START_A + 26然后数字 - START_A
当START_a .. START_a + 26然后(num - START_a)+ 26
当START_0 .. START_0 + 10然后(num - START_0)+ 52
结束
out
结束
开始
而(line = DATA.readline())执行
line.chomp!()
c = compress(line)
a = add_random(c)
output = to_output(a)
input = from_output(output)
new_c, r = remove_random(输入)
u =解压缩(new_c)
printf(原始输入:%s \ n压缩:添加随机数量后%d \ n:%d \ n *输出:%s \ n *输入:%s \ n添加随机数量:删除随机数量后%d \ n:%d \ nnncompressed:%s \ n,line,c,a,输出,输入,r,new_c,u)
结束
救援EOFError => e
结束
__END__
AAA0000
SIN1500
ABD2123
SMS3345
ZZZ9999
使用底部给出的五个输入运行程序会产生此输出:
$ ./compress.rb
原始输入:AAA0000
压缩:0
添加随机数量后:508360097408221184
*输出:liSQkzXL1G
*输入:508360097408221184
随机添加量:508360097408221184
删除随机数量后:0
未压缩:AAA0000
原始输入:SIN1500
压缩: 123891500
添加随机数量后:421470683267231532
*输出:fIVFtX9at2
*输入:421470683267231532
随机数量添加:421470683143340032
移除随机数量后:123891500
未压缩:SIN1500
原始输入:ABD2123
压缩:292123
加入随机数量后:414507907112269083
*输出:emb6JfDhUH
*输入:414507907112269083
rando m金额:414507907111976960
删除随机金额后:292123
未压缩:ABD2123
原始输入:SMS3345
压缩:124983345
加入随机金额后:383242064398325809
*输出:cTPpccLAvn
*输入:383242064398325809
随机数量增加:383242064273342464
删除随机数量后:124983345
未压缩:SMS3345
原始输入:ZZZ9999
压缩:175759999
添加随机数量后:27149937199932031
*输出:CAVf14tiRh
*输入:27149937199932031
随机数量添加:27149937024172032
移除随机数量后:175759999
未压缩:ZZZ9999
您真正想要的行是原始输入
, *输出
,未压缩
。在使用 to_output(add_random(compress(输入)))
之后,您的客户端具有原始输入
行 *输出
行中的十个字符 A-Za-z0-9
输出。你把它交给用户,这是某种神奇的标记。然后,当需要验证它们时,使用 remove_random(from_output(user_string))
来发现添加到字符串和的随机值你可以交给的整数 uncompress()
。
一个非常重要的注释:输入 AAA0000
以明文形式存储在较低的28位中。随机数以高位32位存储在明文中。这只是原始输入的模糊处理,如果有人有两个输入和输出,则发现模式并不困难。哎呀,他们甚至可以正确地猜测只给出一个输入和输出的算法。
如果你需要这个加密强大,那么你还有一些工作要做你的:)但可能就像使用用户名的rc4输出对中间的60位数进行异或那样容易。
简短说明
您的输入字符串可以解释为整数,但:前三个数字位于,接下来的四位数字以10为基数。该数字将小于 175_760_000
。在 0
和 175_760_000
之间唯一存储数字需要 28
位。您的输出字符串也是一个数字,十位数字,基数为62的每个数字。(想想但没有 -
, /
或 =
(用于填充)。)62个可能的值和10个位置给出的最大值 839_299_365_868_340_224
,可以用 60 bits。
输入字符串只需要 28
位来表示,输出字符串 60
位可用,并留下 32
位可用于存储随机生成的数字。如果我们将 32
- 位数乘以 2 ^ 28
(与左移28相同: 1<<<<<<<<<<< 28< / $
一旦我们计算了 60
位数,我们就会以人类消费的基数62表示法输出它。
要反转此过程,请解码基数为62的数字,以获得 60
位数;将 60
位数分成较低的 28
位输入数字和上限 32
位随机数,然后以原始格式输出 28
位数:三个基数26位后跟四个基数10位。
最终更新
Yusuf,将我的Ruby转换为Java的出色工作。我印象非常深刻,特别是考虑到你的Java版本看起来很好:你的版本更清晰。我很嫉妒和印象深刻。 :)
我发现程序中存在两个小错误: RANDOMMASK
被意外初始化为 0
,因为Java 。将 1
更改为 1L
会强制结果 1L<< 60
为长
;没有 L
, 1<<< 60
是一个 int
,并且不足以容纳全部数字。
此外,数字没有被正确压缩;我的Ruby代码将字符解析为整数,并且您的Java代码将字符解释为整数。 (你使用了角色的值;我将角色转换为基于角色的ascii 含义的整数。我不是真的解析,因为它只是做了一个减法,但如果它是一个字符串, String.toInteger(字符)
会做同样的事情,所以很多喜欢解析。)
但是你的解压缩逻辑是正确的,并且由于不匹配,输出不正确。所以我更改了你的代码,将数字解析为整数(并从 char
更改为 int
以使无意义的警告静音) 。
这是我在程序中需要更改的差异,以使其正常工作:
--- 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 -static int RANDOMMASK =((1<< 60)-1)& ~CODEMASK; //打开高32位
+静态长CODEMASK =((1 +静态长RANDOMMASK =((1L
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 =(长)a - START_A;
long small_b =(long)b - START_A;
现在是完整的来源,以防万一更容易:)
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);
静态长CODEMASK =((1 静态长RANDOMMASK =((1L
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);
字符串u = uncompress(Long.valueOf(new_c_r [0]));
System.out.println(原始输入:+ t);
System.out.println(compressed:+ c);
System.out.println(添加随机数量后:+ a);
System.out.println(* output:+ output);
System.out.println(* input:+ input);
System.out.println(随机数量增加:+ new_c_r [1]);
System.out.println(删除随机数量后:+ new_c_r [0]);
System.out.println(未压缩:+ u);
System.out.println(--------------------------------------- --------------------------);
}
}
public static long compress(String line){// 7个字符
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 =(长)a - START_A;
long small_b =(long)b - START_A;
long small_c =(长)c - START_A;
长字母=(small_a * 26 * 26)+(small_b * 26)+ small_c;
长数字=字母* 10000 + h * 1000 + i * 100 + j * 10 + k;
返回数字;
}
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)抛出新的RuntimeException(输入不是用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);
返回result.toString();
}
public static long add_random(long number){
return(((long)(Math.random()* Math.pow(2,31)))< ;< 28)+数字;
}
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);
}
返回result.toString();
}
public static long from_output(String string){
long num = 0;
for(char c:string.toCharArray()){
num * = 62;
num + = transform_in(c);
}
返回num;
}
public static char transform_out(long small){
long out;
if(small< 0 || small> 61){
抛出新的RuntimeException(small应介于0和61之间,包括);
}
if(小于< 26){
out = START_A + small;
}否则if(小于< 52){
out = START_a +(small-26);
}其他{
out = START_0 +(小-52);
}
返回(char)out;
}
public static long transform_in(char c){
if(!String.valueOf(c).matches([a-zA-Z0- 9])){
抛出新的RuntimeException(char应该是AZ,az或0-9,包括);
}
long num =(long)c;
多头;
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抛出新的RuntimeException(Salah,bego!);
退出;
}}
I have a string which contains 3 elements:
- a 3 digit code (example: SIN, ABD, SMS, etc)
- a 1 digit code type (example: 1, 2, 3, etc)
- a 3 digit number (example: 500, 123, 345)
Example string: SIN1500, ABD2123, SMS3345, etc..
I wanted to generate a UNIQUE 10 digit alphanumeric and case sensitive string (only 0-9/a-z/A-Z is allowed), with more than 2^30 (about 1 billion) unique combination per string supplied. 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? Or at least any hint on what keyword should I put on 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?
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. I also tried adding 3 random alphanumeric (so the final digit is 7 + 3 digit = 10 digit). This is what I've done so far, but the result is not very good (only about 100 thousand combination):
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 :
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.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
解决方案 I've written a Ruby tool that does what you need. (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.)
In short, the given input string (7 bytes) is compressed into a number between 0
and 175_760_000
(28 bits). A 32 bit random number is prepended, making a 60 bit integer, which is encoded into a 10-character output string.
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. 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
. 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. 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()
.
One very important note: The input AAA0000
is stored in plaintext in the lower 28 bits. The random number is stored in plaintext in the upper 32 bits. 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.
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
. Uniquely storing numbers between 0
and 175_760_000
requires 28
bits. 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.
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. 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.
Once we've calculated the 60
bit number, we output it in our base 62 notation for human consumption.
To reverse the process, you decode the base 62 number to get our 60
bit number; 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.
FINAL UPDATE
Yusuf, excellent work converting my Ruby to Java. I'm very impressed, especially given how good your Java version looks: your version is more legible. 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. Changing 1
to 1L
forces the result of 1L << 60
to be a long
; without the L
, the result of 1 << 60
is an int
, and isn't large enough to hold the full number.
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. (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.)
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).
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;
}}
这篇关于如何生成超过2 ^ 30组合的随机唯一字符串。我也想扭转这个过程。这可能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!