如何生成随机的字母数字字符串

1968

我一直在寻找一个简单的Java算法来生成伪随机的字母数字字符串。在我的情况下,它将被用作唯一的会话/键标识符,可能在500K+次生成中是唯一的(我的需求并不需要更复杂的东西)。

理想情况下,我希望能够根据我的唯一性需求指定长度。例如,长度为12的生成字符串可能看起来像"AEYGF7K0DM1X"


165
注意生日悖论。 - pablosaraiva
63
即使考虑到生日悖论,如果您使用12个字母数字字符(共62个字符),仍需要超过340亿个字符串才能达到悖论。而且,生日悖论并不保证一定会发生冲突,它只是表示有超过50%的概率。 - NullUserException
6
@NullUserException:每次尝试的成功率达到50%真是太高了,即使尝试10次,成功率也达到0.999。考虑到你可以在24小时内尝试很多次,因此你不需要340亿个字符串就足以确信至少猜中其中一个。这就是为什么某些会话令牌应该非常非常长的原因。 - Pijusn
20
我认为这3个单行代码非常有用。Long.toHexString(Double.doubleToLongBits(Math.random())); 生成一个16进制的随机字符串。UUID.randomUUID().toString(); 生成一个随机唯一标识符。RandomStringUtils.randomAlphanumeric(12); 生成一个包含12个字符的随机字母数字组合。 - Manindar
25
我知道这已经是老话题了,但是,在生日悖论中,“50%的机率”不是“每一次尝试”,而是“存在至少一对重复的机率为50%,在(这种情况下)340亿个字符串中”。你需要拥有1.6*10^21-1.6e21个条目,才能每次尝试有50%的机会。 - Tin Wizard
显示剩余3条评论
46个回答

6
import java.util.*;
import javax.swing.*;

public class alphanumeric {
    public static void main(String args[]) {
        String nval, lenval;
        int n, len;

        nval = JOptionPane.showInputDialog("Enter number of codes you require: ");
        n = Integer.parseInt(nval);

        lenval = JOptionPane.showInputDialog("Enter code length you require: ");
        len = Integer.parseInt(lenval);

        find(n, len);
    }

    public static void find(int n, int length) {
        String str1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        StringBuilder sb = new StringBuilder(length);
        Random r = new Random();

        System.out.println("\n\t Unique codes are \n\n");
        for(int i=0; i<n; i++) {
            for(int j=0; j<length; j++) {
                sb.append(str1.charAt(r.nextInt(str1.length())));
            }
            System.out.println("  " + sb.toString());
            sb.delete(0, length);
        }
    }
}

6
你提到“简单”,但是如果其他人正在寻找更严格的安全要求,你可能需要看一下jpwgen。 jpwgen是Unix中pwgen的模型,并且非常可配置。请参考jpwgenpwgen

谢谢,已修复。至少有源代码并且链接有效。不过缺点是看起来好像有一段时间没有更新了,尽管我看到 pwgen 最近已经更新过。 - michaelok

5

以下是由abacus-common提供的一行代码:

String.valueOf(CharStream.random('0', 'z').filter(c -> N.isLetterOrDigit(c)).limit(12).toArray())

随机并不意味着必须是唯一的。要获取唯一的字符串,请使用:

N.uuid() // E.g.: "e812e749-cf4c-4959-8ee1-57829a69a80f". length is 36.
N.guid() // E.g.: "0678ce04e18945559ba82ddeccaabfcd". length is 32 without '-'

4
如果您的密码必须包含数字和字母特殊字符,则可以使用以下代码:
private static final String NUMBERS = "0123456789";
private static final String UPPER_ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LOWER_ALPHABETS = "abcdefghijklmnopqrstuvwxyz";
private static final String SPECIALCHARACTERS = "@#$%&*";
private static final int MINLENGTHOFPASSWORD = 8;

public static String getRandomPassword() {
    StringBuilder password = new StringBuilder();
    int j = 0;
    for (int i = 0; i < MINLENGTHOFPASSWORD; i++) {
        password.append(getRandomPasswordCharacters(j));
        j++;
        if (j == 3) {
            j = 0;
        }
    }
    return password.toString();
}

private static String getRandomPasswordCharacters(int pos) {
    Random randomNum = new Random();
    StringBuilder randomChar = new StringBuilder();
    switch (pos) {
        case 0:
            randomChar.append(NUMBERS.charAt(randomNum.nextInt(NUMBERS.length() - 1)));
            break;
        case 1:
            randomChar.append(UPPER_ALPHABETS.charAt(randomNum.nextInt(UPPER_ALPHABETS.length() - 1)));
            break;
        case 2:
            randomChar.append(SPECIALCHARACTERS.charAt(randomNum.nextInt(SPECIALCHARACTERS.length() - 1)));
            break;
        case 3:
            randomChar.append(LOWER_ALPHABETS.charAt(randomNum.nextInt(LOWER_ALPHABETS.length() - 1)));
            break;
    }
    return randomChar.toString();
}

4
您可以使用UUID类及其getLeastSignificantBits()方法获取64位随机数据,然后将其转换为36进制数(即由0-9,A-Z组成的字符串):
Long.toString(Math.abs( UUID.randomUUID().getLeastSignificantBits(), 36));

这将产生一个长度最多为13个字符的字符串。我们使用Math.abs()确保没有负号混入。


3
你为什么要使用UUID获取随机位?为什么不直接使用random.nextLong()?或者甚至是Double.doubleToLongBits(Math.random()) - erickson

3
public static String randomSeriesForThreeCharacter() {
    Random r = new Random();
    String value = "";
    char random_Char ;
    for(int i=0; i<10; i++)
    {
        random_Char = (char) (48 + r.nextInt(74));
        value = value + random_char;
    }
    return value;
}

2
字符串拼接是不必要的低效率操作。而且疯狂的缩进使你的代码几乎无法阅读。这与Jamie的想法相同,但执行得很差。 - erickson

3

以下是一个Scala解决方案:

(for (i <- 0 until rnd.nextInt(64)) yield { 
  ('0' + rnd.nextInt(64)).asInstanceOf[Char] 
}) mkString("")

需要解释一下。 - Peter Mortensen

3
使用Apache Commons库,可以在一行代码中完成:
import org.apache.commons.lang.RandomStringUtils;
RandomStringUtils.randomAlphanumeric(64);

Documentation


3
public class Utils {
    private final Random RANDOM = new SecureRandom();
    private final String ALPHABET = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";

    private String generateRandomString(int length) {
        StringBuffer buffer = new StringBuffer(length);
        for (int i = 0; i < length; i++) {
            buffer.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
        }
        return new String(buffer);
    } 
}

1
你能否请添加一些关于此代码片段与其他答案的特殊性的解释? - Raphael D.
这个很干净!喜欢使用SecureRandom。 - jksevend
1
我会用 StringBuilder 替换 StringBuffer,因为 Builder 不是线程安全的,但速度更快。不过还是谢谢你的快速回答!:·) - Roc Boronat

3

我认为这是最小的解决方案之一:

这个方案非常紧凑。
 public String generateRandomString(int length) {
    String randomString = "";

    final char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890".toCharArray();
    final Random random = new Random();
    for (int i = 0; i < length; i++) {
        randomString = randomString + chars[random.nextInt(chars.length)];
    }

    return randomString;
}

这段代码运行良好。如果您使用此方法,建议使用超过10个字符。在5个字符/30362次迭代时发生碰撞。这需要9秒钟。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接