简体   繁体   中英

How to generate random alphanumeric string from a given string in C#?

I have a string. I want to generate a random string from this string, by replacing a number by a random number. lower character by lower character and upper character by upper character. And remaining characters as it is.

I have written the below code. I need to call this method millions of time on different strings (string length is not more than 100 characters), It's taking too much time.

private static string GenerateRandomAlphanumericValue(string input) {
    char[] newStr = new char[input.Length];
    char[] alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
    char[] alphaL = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
    char[] number = "0123456789".ToCharArray();
    Random random = new Random();
    for (int i = 0; i < input.Length; i++) {
        if (char.IsNumber(input[i])) {

            int index = random.Next(0, number.Length);
            newStr[i] = number[index];
        }
        else if (char.IsUpper(input[i])) {
            int index = random.Next(0, alphaU.Length);
            newStr[i] = alphaU[index];
        }
        else if (char.IsLower(input[i])) {
            int index = random.Next(0, alphaL.Length);
            newStr[i] = alphaL[index];
        }
        else {
            newStr[i] = input[i];
        }
    }

    return string.Join("", newStr);
}

I need help in optimizing the code or there any be a different approach to solve the problem.

Input: vivEK123$% ~a

Output: ajrLW854$% ~w

Try this

static char[] alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
static char[] alphaL = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
static char[] number = "0123456789".ToCharArray();
static Random random = new Random();
static StringBuilder sb = new StringBuilder(100);

private static string GenerateRandomAlphanumericValue(string input)
{
    sb.Clear();
    for (int i = 0; i < input.Length; i++)
    {
        if (char.IsNumber(input[i]))
        {
            int index = random.Next(0, number.Length);
            sb.Append(number[index]);
        }
        else if (char.IsUpper(input[i]))
        {
            int index = random.Next(0, alphaU.Length);
            sb.Append(alphaU[index]);
        }
        else if (char.IsLower(input[i]))
        {
            int index = random.Next(0, alphaL.Length);
            sb.Append(alphaL[index]);
        }
        else
        {
            sb.Append(input[i]);
        }
    }

    return sb.ToString();
}

Instead of using arrays, you could just randomize based on the ASCII-value range of characters. I'd also suggest using StringBuilder to build the result string.

public class Randomizer{
    private static Random rng = new Random();
    public static string RandomizeString(string input){
        StringBuilder sb = new StringBuilder();
        foreach(char c in input){
            if(Char.IsNumber(c)){
                sb.Append(rng.Next(0,10));
            }
            else if(Char.IsLower(c)){
                sb.Append((char)rng.Next(97,123));
            }
            else if(Char.IsUpper(c)){
                sb.Append((char)rng.Next(65,91));
            }
            else{
                sb.Append(c);
            }
        }
        return sb.ToString();
    }
}

Note: az is ASCII 97-122 , AZ is ASCII 65-90 , and 0-9 is just an integer cast to a string.

This is a little more efficient. I'm not sure about execution time :

       private static string GenerateRandomAlphanumericValue(string input)
        {
            string newStr = "";
            string alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string alphaL = "abcdefghijklmnopqrstuvwxyz";
            string number = "0123456789";
            Random random = new Random();
            for (int i = 0; i < input.Length; i++)
            {
                if (char.IsNumber(input[i]))
                {

                    int index = random.Next(0, number.Length);
                    newStr  += number[index];
                }
                else if (char.IsUpper(input[i]))
                {
                    int index = random.Next(0, alphaU.Length);
                    newStr += alphaU[index];
                }
                else if (char.IsLower(input[i]))
                {
                    int index = random.Next(0, alphaL.Length);
                    newStr += alphaL[index];
                }
                else
                {
                    newStr += input[i];
                }
            }

            return newStr;
        }

The general principle is to move as much as possible out of loops and to use the least expensive techniques inside loops.

Random only needs to be constructed once.

StringBuilder is one of the more efficient ways of moving individually acquire Chars into a String. And, it needs to be constructed only once (but introduces thread safety issues).

Array indexing is one fast way to replace chain logic but might not be the fastest since there are only three ranges. ( if >= && <= times three could be faster.)

IsUpper et al might have a big impact on its performance. They have to account for all Unicode codepoints in the general category Lu, Ll or Nd.

private static readonly Random random = new Random();
private static readonly StringBuilder sb = new StringBuilder(100);

// min and max are the replacement range for indexing Char.
private static readonly Char[] min = new Char[Char.MaxValue + 1];
private static readonly Char[] max = new Char[Char.MaxValue + 1];

static UserQuery()
{
    foreach (var range in new[] { 
        (min: 'A', max: 'Z'), 
        (min: 'a', max: 'z'), 
        (min: '0', max: '9') })
    {
        for (var c = range.min; c <= range.max; c++)
        {
            min[c] = range.min;
            max[c] = range.max;
        }
    }
}

private static String GenerateRandomAlphanumericValue(String input)
{
    sb.Clear();
    foreach (var c in input)
    {
        sb.Append((Char)random.Next(min[c], max[c]));
    }
    return sb.ToString();  
}

The following code is derived from Bahrom's answer with commented changes against your original and Bahrom's code :

//moving out the static initialization cut down the execution time drastically
//since now there's no need repeatedly create these for each execution.
static char[] alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
static char[] alphaL = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
static char[] number = "0123456789".ToCharArray();
static Random random = new Random();
//moving out Random constructor also remove duplicate result if using identical input
//in close sequence
static char[] temp = new char[100]; //tiny edge compared to using StringBuilder
private static string GenerateRandomAlphanumericValue2(string input)
{            
    for (int i = 0; i < input.Length; i++)
    {
        if (input[i] < '0' || input[i] > 'z')
        //exploit the fact that number, upper case and lower case letter
        //are located between 0 and lower-case z, so anything outside it is
        //left as they are
        {
            temp[i]=(input[i]);
        }
        else if (input[i] <= '9') //no need to check for >='0' again
        {
            temp[i]=(number[random.Next(10)]);
            //using constant size instead of calling length
        }
        else if (input[i] >= 'A' && input[i] <= 'Z')
        {
            temp[i]=(alphaU[random.Next(0, 26)]);
        }
        else if (input[i] >= 'a') //no need to check for <='z' again
        {
            temp[i]=(alphaL[random.Next(0, 26)]);
        }
        else
        {
            temp[i]=(input[i]);
        }
    }

    return new string(temp, 0, input.Length);

}

Now, despite all the harder-to-read optimization against Bahrom's elegant use of StringBuilder and your IsNumber, IsUpper and IsLower, when run against a million strings each containing between 0 to 100 character, your code take about 7 seconds, while Bahrom's only take around 2.5 seconds and my code take slightly below 2 seconds. This is because StringBuilder is already very optimized for string manipulation, and while IsNumber, IsUpper and IsLower do have to consider Unicode number & letter, their implementation only need two if's to check if a character is Latin & Ascii , before proceeding with simple return (c >= '0' && c <= '9'); etc. Thus, if you want to keep your code readable, stick with Bahrom's implementation. Only use mine if you really need to squeeze the very last performance (processing hundreds of millions strings?)

Put the temp declaration back inside, and the code is parallel friendly, though I just can't get faster execution since the overhead of declaring temp over-and-over or using object-pool pattern is more than the parallel speed-up.

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