views:

397

answers:

3

I found this snippet of code that generates a string of random characters:
http://www.c-sharpcorner.com/UploadFile/mahesh/RandomNumber11232005010428AM/RandomNumber.aspx

But is there a more elegant/faster/more reliable way to do this? This seems to rely on the fact that the numbers 26-91 are valid characters given the current encoding.

/// <summary>
/// Generates a random string with the given length
/// </summary>
/// <param name="size">Size of the string</param>
/// <param name="lowerCase">If true, generate lowercase string</param>
/// <returns>Random string</returns>
private string RandomString(int size, bool lowerCase)
{
    StringBuilder builder = new StringBuilder();
    Random random = new Random();
    char ch;

    for(int i = 0; i < size; i++)
    {
        ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
        builder.Append(ch);
    }

    if(lowerCase)
        return builder.ToString().ToLower();

    return builder.ToString();
}
+10  A: 

I'd prefer to pass the Random instance into the method - then you can reuse the same instance multiple times, which is important if you need to generate lots of random strings in quick succession. However, I'd also modify it somewhat anyway:

public const string LowerCaseAlphabet = "abcdefghijklmnopqrstuvwyxz";
public const string UpperCaseAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

public static string GenerateUpperCaseString(int size, Random rng)
{
    return GenerateString(size, rng, UpperCaseAlphabet);
}

public static string GenerateLowerCaseString(int size, Random rng)
{
    return GenerateString(size, rng, LowerCaseAlphabet);
}

public static string GenerateString(int size, Random rng, string alphabet)
{
    char[] chars = new char[size];
    for (int i=0; i < size; i++)
    {
        chars[i] = alphabet[rng.Next(alphabet.Length)];
    }
    return new string(chars);
}
  • There's no need to use a StringBuilder when you know the final length
  • Using Random.NextDouble() indicates a lack of knowledge of the Random class. (In particular Random.Next(int, int)
  • Creating a new Random on each call is likely to result in duplicate strings
  • Calling Convert.ToInt32 and Convert.ToChar seems ugly compared with just casting
  • Lower-casing afterwards seems pointless compared with picking lower case letters to start with
  • Providing an alphabet to pick from is a lot more flexible (with helper methods for common cases)
Jon Skeet
Yup, I think this is much better. Just create a string full of your acceptable characters, then randomly pick characters from that string, building your own.I think the above example could be simplified, by not requiring the rng and alphabet parameters to be passed to the method, but it gives you the idea.
Jeremy
@Jeremy: Passing in the RNG is *very* important unless you want a method which is likely to create duplicate strings. You *could* keep a static Random instance, but then you'd need to worry about locking as well.
Jon Skeet
Excellent answer as always Jon. Couple other things to consider. (1) How random is random enough? Given just one string of sufficient length from your version is enough data to deduce what every future output will be, provided the attacker knows the size, alphabet, and whether the same Random is re-used. This level of randomness is not suitable for crypto uses, generating passwords, etc. (2) Are these random strings going to be displayed to the user? I can think of a number of random outputs that users might find offensive or off-putting. Might want to filter for rude words.
Eric Lippert
Some additional thoughts in this vein (re VBScript, but the issues are pretty much the same) are here: http://blogs.msdn.com/gstemp/archive/2004/02/23/78434.aspx
Eric Lippert
I think I'm being stalked ;) All good points. I can think of interesting ways of making it a bit harder (such as keeping a "master" Random, and generating new randoms from that based on seeds generated by the master) but all are likely to have similar problems. As for using a crypto-strength RNG... isn't it a shame that we don't have *one* Random basse class, with different implementations? (Instead of System.Random and System.Security.Cryptography.RandomNumberGenerator.) Then the caller could decide the security level they want. Like Java's Random/SecureRandom :) (Continued)
Jon Skeet
I've been irked by the design of Random before, actually. It would be nice to have a thread-safe implementation *or* a design which lets a derived class provide a thread-safe "core" that is used by the rest. Ah well. As for rude word filtering... ick, tricky stuff. I remember looking at the offensive word filter back when I was working at Clearswift. I hadn't heard of half the words... and the office was amused to note that Skeet was on there, too... (Again, I'd been blissfully ignorant.)
Jon Skeet
A: 

Did you read this article by Ayende? Just wondering :)

tanascius
That is just plain mean! Especially for the dev who inherits the project!
SkippyFire
+1  A: 

If I were to do this (and I have, but in Java, squirreled away somewhere), I would provide an array of allowable characters, and use a RNG simply to pick the index of the character. This would also let you disallow characters you don't want to generate (if you're creating a human-enterable license key, you don't want to generate characters that can be confused with each other; 0 and O, for instance, or 1 and l).

EDIT: yeah, like what Jon did...

Michael Petrotta
+1 for effort :)
SkippyFire