简体   繁体   中英

Generating random Strings using StringBuilder results in Strings with length less than predefined length

I am generating a number of random Strings, with a specified length, using for loops and a StringBuilder, but I am getting Strings that are shorter than the specified length. My question is, why are some Strings being generated with the correct length, and some are not?

public class PasswordGen {

    public static void main(final String[] args) {
        final String[] chars = characters.split("");
        final List<String> characterList = Arrays.asList(chars);
        final Random rng = new SecureRandom();
        Collections.shuffle(characterList, rng);
        final int passwordsToGenerate = 16;
        final int length = 31;
        for (int i = 0; i < passwordsToGenerate; i++) {
            final StringBuilder builder = new StringBuilder();
            for (int j = 0; j < length; j++) {
                final int index = rng.nextInt(characterList.size());
                builder.append(characterList.get(index));
            }
            final String password = builder.toString();
            System.out.println(password);
            if (password.length() < 31) {
                System.out.println("^ is not 31 characters in length: " + password.length());
            }
        }

    }

    public static final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
}

The output:

LMUYN7bHKX13Rx1dwEkbHI8KjUONbDN
hnwMBD2LPHyuoYffGy7eC5STAg8heB
^ is not 31 characters in length: 30
EbburpyvZqc71mPadP8rYc6cJ8K9wl
^ is not 31 characters in length: 30
nLb58OzfnERsPm1SdR1nKBuOilhXzG
^ is not 31 characters in length: 30
c44M4Pk1kDIBMVqL0o5NybPA5t8zI3D
I3ttG7TvO8pKOp9yb5mGzJtnkTNVLH9
K1md1ORt2W3s8iR3RpMf2yNHjMrH8N
^ is not 31 characters in length: 30
G8daUMVLoMqpYqSrAUzSdI5IJCM7GSM
KMfaLvfCaudpRACpuOlA1gO3rqmhBs
^ is not 31 characters in length: 30
Y20V5RcKUiS8Ny9NpARmTMac29y8Z
^ is not 31 characters in length: 29
Rse101oxhOMXRdpchp4X3Uzm00Gzsc
^ is not 31 characters in length: 30
oQxaYE9qx1cEJP8KvTGdwo1IJhNH41O
y6E2WrKEAl2zWV6BCkH1JaWvz3aBxvs
QFautBHKlgDPlJhmge7n6Kf79G6mfu
^ is not 31 characters in length: 30
l2OBC9sFsZOfB481HXLsIiIwnmcOpdB
A4CTQj9Xc7grxtYYFzDRLelf3YQOTWV

I tried adding a Thread.sleep() block inside both the inner for loop, before and after getting the next index, before and after appending the character to the builder, before and after setting the password variable, and before and after the the inner loop, to see if it was somehow related to threading. There was no visible difference in the results.

I am using Windows 7 Professional 64-bit, and have tested with JDK 7u67 and 8u11, both 32-bit and 64-bit with the same results.

Don't split characters by empty string, just use getChars method. split by empty string behaviour not same in different versions of java. For example: Java split string on empty delimiter returns empty string at the beginning?

The problem is due to characters.split(""); , which will return an array with the first entry being empty string for Java version 7 and before. It will return the expected result for Java 8 and up .

You can use characters.split("(?!^)"); to split a string into characters . This method works on every version of Java. The split() function expects a regex, and the regex above does not match at the beginning of the string while matching everywhere else, which avoids producing an empty string as the first entry altogether.

Or as a cleaner alternative, get an array of all characters with String.toCharArray() .

In your split() call, the empty regex matches the start and the end of characters . This results in two "" entries in characterList . Whenever they get picked, you end up with short output.

Try this code :

static final String AlphaNumeric = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static Random rnd = new Random();


public static String randomString (int len){
     StringBuilder sb = new StringBuilder(len);
     for( int i = 0; i < len; i++ )
        sb.append( AlphaNumeric.charAt( rnd.nextInt(AlphaNumeric.length()) ) );
        return sb.toString();
}

you can add other wild card character or small cases to AlphaNumeric to generate more complex or unique token.

call the function & up to whatever length, it will generate random string for you.

Here's a running example of it.

Your problem lies in the use of split() which also adds to String[] chars the empty String, you can check this easily by checking chars.length which is 63.

The code below must do what you want:

public class PasswordGen {

public static final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

public static void main(final String[] args) {
    final int characterListSize = characters.length();
    char[] arr = characters.toCharArray();
    List<Character> characterList = new ArrayList<Character>();
    for (char c : arr)
        characterList.add(c);

    // System.out.println(characterList);
    final Random rng = new SecureRandom();
    Collections.shuffle(characterList, rng);
    final int passwordsToGenerate = 16;
    final int length = 31;
    for (int i = 0; i < passwordsToGenerate; i++) {
        final StringBuilder builder = new StringBuilder();
        for (int j = 0; j < length; j++) {
            int index = rng.nextInt(characterListSize);
            builder.append(characters.charAt(index));
        }
        final String password = builder.toString();
        if (password.length() < 31) {
            System.out.println("^ is not 31 characters in length: "
                    + password.length());
        }
    }

}

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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