使用C#中的条件生成随机字母数字字符串

时间:2022-11-24 21:49:55

According to following code , I need to generate a string in different cases based on the method input. My issue is where I wanna generate 9A9A (at least 1 number and 1 letter) or 9A9A9A (at least 2 numbers and 2 letters). In most cases, this conditions is not met.

根据以下代码,我需要根据方法输入在不同的情况下生成一个字符串。我的问题是我想生成9A9A(至少1个数字和1个字母)或9A9A9A(至少2个数字和2个字母)。在大多数情况下,不满足这些条件。

private AuthMessage GetAuthCode(string CodeType) //(out string Message)
    {
        Guid Guid = Guid.NewGuid();
        Random Random = new Random();
        string AuthCode = string.Empty;
        string RefCode = string.Empty;

        RefCode = Guid.ToString("N");

        switch (CodeType)
        {
            case "0": //9999
                {
                    AuthCode = Random.Next(1000, 9999).ToString();
                    break;
                }
            case "1": //99999
                {
                    AuthCode = Random.Next(10000, 99999).ToString();
                    break;
                }
            case "2": //999999
                {
                    AuthCode = Random.Next(100000, 999999).ToString();
                    break;
                }
            case "3": //999-999
                {
                    AuthCode = Regex.Replace(Random.Next(100000, 999999).ToString(), @"^(.{3})(.{3})$", "$1-$2");
                    break;
                }
            case "4": //9A9A
                {
                    AuthCode = Guid.ToString("N").Substring(14, 4).ToUpper();
                    break;
                }
            case "5": //9A9A9
                {
                    AuthCode = Guid.ToString("N").Substring(15, 5).ToUpper();
                    break;
                }
            case "6": //9A9A9A
                {
                    AuthCode = Guid.ToString("N").Substring(6, 6).ToUpper();
                    break;
                }
            case "7": //9A9-A9A
                {
                    AuthCode = Regex.Replace(Guid.ToString("N").Substring(6, 6), @"(.{3})(.{3})", @"$1-$2").ToUpper();
                    break;
                }
            case "8": //9A9-A9A
                {
                    AuthCode = Regex.Replace(Regex.Replace(Convert.ToBase64String(Guid.ToByteArray()), "[/+=]", "").Substring(0, 6), @"(.{3})(.{3})", @"$1-$2").ToUpper();
                    break;
                }
            default:
                {
                    AuthCode = Random.Next(1000, 9999).ToString();
                    break;
                }
        }

        AuthMessage Response = new AuthMessage();
        Response.AuthCode = AuthCode;
        Response.RefCode = RefCode;

     return Response;
    }

5 个解决方案

#1


2  

I thought I'd have a go - it gives me a good chance of being ridiculed. This isn't the most efficient way of generating the codes, but it should be fairly random.

我以为我会去 - 它给了我一个被嘲笑的好机会。这不是生成代码的最有效方法,但它应该是相当随机的。

private string GetAuthCode(string CodeType)
{
    var patterns = new Dictionary<char, Func<Char>>()
    {
        { '9', () => RandomBytes().Where(x => x >= '0' && x <= '9').First() },
        { 'A', () => RandomBytes().Where(x => x >= 'A' && x <= 'Z').First() },
        { '-', () => '-' },
    };

    return
        String.IsNullOrEmpty(CodeType)
            ? ""
            : patterns[CodeType[0]]().ToString() + GetAuthCode(CodeType.Substring(1));
}

private IEnumerable<char> RandomBytes()
{
    using (var rng = System.Security.Cryptography.RNGCryptoServiceProvider.Create())
    {
        var bytes = new byte[256];
        while (true)
        {
            rng.GetBytes(bytes);
            foreach (var @byte in bytes)
            {
                yield return (char)@byte;
            }
        }
    }
}

Now, due to the funky monkey state machine that implements iterator methods, this code does dispose of the RNG despite the while (true).

现在,由于实现迭代器方法的时髦的猴子状态机,这个代码尽管处理了while(true),但它确实处理了RNG。

I simplified the GetAuthCode method slightly, but I think this demonstrates a suitable way to generate the codes.

我略微简化了GetAuthCode方法,但我认为这证明了生成代码的合适方法。

#2


4  

Guid representation is composed of hexadecimal digits, i.e. characters 0-9 and a-f. The problem with relying on it to obtain a mixture of letters and numbers is that a character at any given position could be either a letter or a decimal digit, with probability tilted roughly 5:3 in favor of a decimal digit.

Guid表示由十六进制数字组成,即字符0-9和a-f。依靠它来获得字母和数字的混合的问题在于,任何给定位置处的字符可以是字母或十进制数字,概率倾斜大约5:3而有利于十进制数字。

If you want to generate a specific mix of digits and letters, you should generate the string one character at a time, without relying on Guid representation.

如果要生成特定的数字和字母组合,则应该一次生成一个字符,而不依赖于Guid表示。

#3


-2  

public class TokenCreator
{
 private Random random = new Random();
 private const string[] chars= "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 public static string CreateToken(int length)
 {
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
 }
}

AuthCode = TokenCreator.CreateToken(5);

This implementation can little help you. But again it doesn't guarantee you get alphanumeric combination. Sometime you may get only alphabets and sometime you may get only numbers. Though chances are so little.

这种实现对您没什么帮助。但同样,它并不能保证你得到字母数字组合。有时您可能只获得字母表,有时您可能只获得数字。虽然机会很少。

#4


-2  

OverView The following code uses Regex.Replace along woth IsMatch which allows one to create an any length pattern such as XXXXXX or XX-XX which will replace any X with a random character or letter.

OverView以下代码使用Regex.Replace和IsMatch一起使用,它允许创建任何长度模式,例如XXXXXX或XX-XX,它将用随机字符或字母替换任何X.

For example XX-XX could return A(-1d or with dashes just XXXX to A(1d.

例如,XX-XX可以返回A(-1d或短划线只是XXXX到A(1d。

Security It uses the Web.Security.Membership.GeneratePassword to get a string of 48 random characters, digits and what not. Then a clear password is culled from that random characters based on the pattern needed.

安全性它使用Web.Security.Membership.GeneratePassword获取48个随机字符,数字和不包含的字符串。然后根据所需的模式从该随机字符中剔除清晰的密码。

(at least 1 number and 1 letter)

(至少1个数字和1个字母)

This is done in the validation regex which assures there is atleast one alphabetic character and one number. That generate method is called until that validation reports a valid match from the rules you mentioned.

这是在验证正则表达式中完成的,它确保至少有一个字母字符和一个数字。调用该generate方法,直到该验证报告您提到的规则的有效匹配。

Secure Transport Finally a secure string is setup to be returned.

安全传输最后设置一个安全字符串返回。

This one works off of getting a

这个可以解决这个问题

// This pattern enforces our rule that a pwd must have one letter and one digit.
string pattern = @" # This regex pattern enforces the rules before returning matching
(?=.*[a - zA - Z])  # Somewhere there is a an alphabectic character
(?=.*\d)            # Somewhere there is a number; if no number found return no match.
(.+)                # Successful match, rules are satisfied. Return match";

Random rn = new Random(); // Used to cherry pick from chars to use.

// Creates 48 alpha and non alpha (at least 10 non digit alphas) random characters.
string charsToUse = System.Web.Security.Membership.GeneratePassword(48, 5);

// When replacement is done, replace an `X` matched with a random char. 
MatchEvaluator RandomChar = delegate (Match m)
{
    return charsToUse[rn.Next(charsToUse.Length)].ToString();
};

Func<string, string> Validate = 
      (string str) => Regex.IsMatch(str, pattern, RegexOptions.IgnorePatternWhitespace) 
                      ? str : string.Empty; // return empty on failure.

string pwdClear = string.Empty;

// Generate valid pwd based on rules. Loop until rules are met.
while (string.IsNullOrEmpty(pwdClear))
    pwdClear = Validate(Regex.Replace("XXXX-XXXX-XXXX-XXXX-XXXX", "X", RandomChar));

// Create a secure string for the password for transportation.
SecureString ss = new SecureString();

pwdClear.ToList()
        .ForEach(chr => ss.AppendChar(chr));

This answer is based off of a non-secure implementation on my blog see C#: Generate a Random Sequence of Numbers and Letters From a User Defined Pattern and Characters

这个答案基于我博客上的非安全实现,请参阅C#:从用户定义的模式和字符生成数字和字母的随机序列

#5


-2  

For testing purposes, when I need to generate random strings with specific properties, I use something similar to this. You might have to adapt it to your needs.

出于测试目的,当我需要生成具有特定属性的随机字符串时,我使用类似于此的东西。您可能必须根据自己的需要进行调整。

public sealed class StringGenerator
{
  private static readonly char[] NumericChars = "0123456789".ToCharArray();
  private static readonly char[] LowerAlphaChars = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
  private static readonly char[] UpperAlphaChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();

  public StringGenerator(IRandom rnd)
  {
    Rnd = rnd ?? new SecureRandom();
  }

  private IRandom Rnd
  {
    get;
    set;
  }

  public string Generate(int length, int minNumeric = 0, int minAlpha = 0, AlphaCase alphaCase = AlphaCase.Both)
  {
    if (length < 0)
    {
      throw new ArgumentOutOfRangeException("length");
    }
    if (minNumeric < 0)
    {
      throw new ArgumentOutOfRangeException("minNumeric");
    }
    if (minAlpha < 0)
    {
      throw new ArgumentOutOfRangeException("minAlpha");
    }
    if (length < minNumeric + minAlpha)
    {
      throw new ArgumentException();
    }

    if (length == 0)
    {
      return string.Empty;
    }

    var result = new char[length];

    var index = 0;

    foreach(var numeric in GenerateNumeric().Take(minNumeric))
    {
      result[index++] = numeric;
    }

    var alphaCharacters = GetAlphaCharacters(alphaCase);
    foreach (var alpha in Generate(alphaCharacters).Take(minAlpha))
    {
      result[index++] = alpha;
    }


    var restLength = length - index;

    if (restLength > 0)
    {
      var restCharacters = new List<char>(NumericChars.Concat(alphaCharacters));

      foreach (var rest in Generate(restCharacters).Take(restLength))
      {
        result[index++] = rest;
      }
    }

    // shuffle result
    return new string(result.OrderBy(x => Rnd.Next()).ToArray());
  }

  private IList<char> GetAlphaCharacters(AlphaCase alphaCase)
  {
    switch (alphaCase)
    {
      case AlphaCase.Lower:
        return LowerAlphaChars;

      case AlphaCase.Upper:
        return UpperAlphaChars;

      case AlphaCase.Both:
      default:
        return new List<char>(LowerAlphaChars.Concat(UpperAlphaChars));
    }
  }

  public IEnumerable<char> GenerateNumeric()
  {
    return Generate(NumericChars);
  }
  public IEnumerable<char> GenerateLowerAlpha()
  {
    return Generate(LowerAlphaChars);
  }
  public IEnumerable<char> GenerateUpperAlpha()
  {
    return Generate(UpperAlphaChars);
  }

  public IEnumerable<char> Generate(IList<char> characters)
  {
    if (characters == null)
    {
      throw new ArgumentNullException();
    }
    if (!characters.Any())
    {
      yield break;
    }

    while (true)
    {
      yield return characters[Rnd.Next(characters.Count)];
    }
  }
}

public enum AlphaCase
{
  Lower,
  Upper,
  Both
}

public interface IRandom
{
    int Next();
    int Next(int maxValue);
}

public sealed class SecureRandom : IRandom
{
    private readonly RandomNumberGenerator Rng = new RNGCryptoServiceProvider();

    public int Next()
    {
        var data = new byte[sizeof(int)];
        Rng.GetBytes(data);
        return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1);
    }

    public int Next(int maxValue)
    {
        return Next(0, maxValue);
    }

    public int Next(int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException();
        }
        return (int)Math.Floor(minValue + ((double)maxValue - minValue) * NextDouble());
    }

    public double NextDouble()
    {
        var data = new byte[sizeof(uint)];
        Rng.GetBytes(data);
        var randomUint = BitConverter.ToUInt32(data, 0);
        return randomUint / (uint.MaxValue + 1d);
    }
}

Edit: This is an answer to the question of how to generate random alphanumeric strings with conditions. As stated for testing purposes. Not to be used as is in a context where security is relevant.

编辑:这是如何生成带条件的随机字母数字字符串的问题的答案。如测试目的所述。不得在安全性相关的环境中使用。


Edit 2: Shamelessly borrowing from this answer, the solution now uses a wrapper around RNGCryptoServiceProvider. Still not sure if this should be used in security relevant context, but at least now it should be "better" than simply using Random

编辑2:从这个答案无耻地借鉴,解决方案现在使用RNGCryptoServiceProvider周围的包装器。仍然不确定是否应该在安全相关的上下文中使用它,但至少现在它应该比简单地使用Random更“好”

#1


2  

I thought I'd have a go - it gives me a good chance of being ridiculed. This isn't the most efficient way of generating the codes, but it should be fairly random.

我以为我会去 - 它给了我一个被嘲笑的好机会。这不是生成代码的最有效方法,但它应该是相当随机的。

private string GetAuthCode(string CodeType)
{
    var patterns = new Dictionary<char, Func<Char>>()
    {
        { '9', () => RandomBytes().Where(x => x >= '0' && x <= '9').First() },
        { 'A', () => RandomBytes().Where(x => x >= 'A' && x <= 'Z').First() },
        { '-', () => '-' },
    };

    return
        String.IsNullOrEmpty(CodeType)
            ? ""
            : patterns[CodeType[0]]().ToString() + GetAuthCode(CodeType.Substring(1));
}

private IEnumerable<char> RandomBytes()
{
    using (var rng = System.Security.Cryptography.RNGCryptoServiceProvider.Create())
    {
        var bytes = new byte[256];
        while (true)
        {
            rng.GetBytes(bytes);
            foreach (var @byte in bytes)
            {
                yield return (char)@byte;
            }
        }
    }
}

Now, due to the funky monkey state machine that implements iterator methods, this code does dispose of the RNG despite the while (true).

现在,由于实现迭代器方法的时髦的猴子状态机,这个代码尽管处理了while(true),但它确实处理了RNG。

I simplified the GetAuthCode method slightly, but I think this demonstrates a suitable way to generate the codes.

我略微简化了GetAuthCode方法,但我认为这证明了生成代码的合适方法。

#2


4  

Guid representation is composed of hexadecimal digits, i.e. characters 0-9 and a-f. The problem with relying on it to obtain a mixture of letters and numbers is that a character at any given position could be either a letter or a decimal digit, with probability tilted roughly 5:3 in favor of a decimal digit.

Guid表示由十六进制数字组成,即字符0-9和a-f。依靠它来获得字母和数字的混合的问题在于,任何给定位置处的字符可以是字母或十进制数字,概率倾斜大约5:3而有利于十进制数字。

If you want to generate a specific mix of digits and letters, you should generate the string one character at a time, without relying on Guid representation.

如果要生成特定的数字和字母组合,则应该一次生成一个字符,而不依赖于Guid表示。

#3


-2  

public class TokenCreator
{
 private Random random = new Random();
 private const string[] chars= "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 public static string CreateToken(int length)
 {
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
 }
}

AuthCode = TokenCreator.CreateToken(5);

This implementation can little help you. But again it doesn't guarantee you get alphanumeric combination. Sometime you may get only alphabets and sometime you may get only numbers. Though chances are so little.

这种实现对您没什么帮助。但同样,它并不能保证你得到字母数字组合。有时您可能只获得字母表,有时您可能只获得数字。虽然机会很少。

#4


-2  

OverView The following code uses Regex.Replace along woth IsMatch which allows one to create an any length pattern such as XXXXXX or XX-XX which will replace any X with a random character or letter.

OverView以下代码使用Regex.Replace和IsMatch一起使用,它允许创建任何长度模式,例如XXXXXX或XX-XX,它将用随机字符或字母替换任何X.

For example XX-XX could return A(-1d or with dashes just XXXX to A(1d.

例如,XX-XX可以返回A(-1d或短划线只是XXXX到A(1d。

Security It uses the Web.Security.Membership.GeneratePassword to get a string of 48 random characters, digits and what not. Then a clear password is culled from that random characters based on the pattern needed.

安全性它使用Web.Security.Membership.GeneratePassword获取48个随机字符,数字和不包含的字符串。然后根据所需的模式从该随机字符中剔除清晰的密码。

(at least 1 number and 1 letter)

(至少1个数字和1个字母)

This is done in the validation regex which assures there is atleast one alphabetic character and one number. That generate method is called until that validation reports a valid match from the rules you mentioned.

这是在验证正则表达式中完成的,它确保至少有一个字母字符和一个数字。调用该generate方法,直到该验证报告您提到的规则的有效匹配。

Secure Transport Finally a secure string is setup to be returned.

安全传输最后设置一个安全字符串返回。

This one works off of getting a

这个可以解决这个问题

// This pattern enforces our rule that a pwd must have one letter and one digit.
string pattern = @" # This regex pattern enforces the rules before returning matching
(?=.*[a - zA - Z])  # Somewhere there is a an alphabectic character
(?=.*\d)            # Somewhere there is a number; if no number found return no match.
(.+)                # Successful match, rules are satisfied. Return match";

Random rn = new Random(); // Used to cherry pick from chars to use.

// Creates 48 alpha and non alpha (at least 10 non digit alphas) random characters.
string charsToUse = System.Web.Security.Membership.GeneratePassword(48, 5);

// When replacement is done, replace an `X` matched with a random char. 
MatchEvaluator RandomChar = delegate (Match m)
{
    return charsToUse[rn.Next(charsToUse.Length)].ToString();
};

Func<string, string> Validate = 
      (string str) => Regex.IsMatch(str, pattern, RegexOptions.IgnorePatternWhitespace) 
                      ? str : string.Empty; // return empty on failure.

string pwdClear = string.Empty;

// Generate valid pwd based on rules. Loop until rules are met.
while (string.IsNullOrEmpty(pwdClear))
    pwdClear = Validate(Regex.Replace("XXXX-XXXX-XXXX-XXXX-XXXX", "X", RandomChar));

// Create a secure string for the password for transportation.
SecureString ss = new SecureString();

pwdClear.ToList()
        .ForEach(chr => ss.AppendChar(chr));

This answer is based off of a non-secure implementation on my blog see C#: Generate a Random Sequence of Numbers and Letters From a User Defined Pattern and Characters

这个答案基于我博客上的非安全实现,请参阅C#:从用户定义的模式和字符生成数字和字母的随机序列

#5


-2  

For testing purposes, when I need to generate random strings with specific properties, I use something similar to this. You might have to adapt it to your needs.

出于测试目的,当我需要生成具有特定属性的随机字符串时,我使用类似于此的东西。您可能必须根据自己的需要进行调整。

public sealed class StringGenerator
{
  private static readonly char[] NumericChars = "0123456789".ToCharArray();
  private static readonly char[] LowerAlphaChars = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
  private static readonly char[] UpperAlphaChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();

  public StringGenerator(IRandom rnd)
  {
    Rnd = rnd ?? new SecureRandom();
  }

  private IRandom Rnd
  {
    get;
    set;
  }

  public string Generate(int length, int minNumeric = 0, int minAlpha = 0, AlphaCase alphaCase = AlphaCase.Both)
  {
    if (length < 0)
    {
      throw new ArgumentOutOfRangeException("length");
    }
    if (minNumeric < 0)
    {
      throw new ArgumentOutOfRangeException("minNumeric");
    }
    if (minAlpha < 0)
    {
      throw new ArgumentOutOfRangeException("minAlpha");
    }
    if (length < minNumeric + minAlpha)
    {
      throw new ArgumentException();
    }

    if (length == 0)
    {
      return string.Empty;
    }

    var result = new char[length];

    var index = 0;

    foreach(var numeric in GenerateNumeric().Take(minNumeric))
    {
      result[index++] = numeric;
    }

    var alphaCharacters = GetAlphaCharacters(alphaCase);
    foreach (var alpha in Generate(alphaCharacters).Take(minAlpha))
    {
      result[index++] = alpha;
    }


    var restLength = length - index;

    if (restLength > 0)
    {
      var restCharacters = new List<char>(NumericChars.Concat(alphaCharacters));

      foreach (var rest in Generate(restCharacters).Take(restLength))
      {
        result[index++] = rest;
      }
    }

    // shuffle result
    return new string(result.OrderBy(x => Rnd.Next()).ToArray());
  }

  private IList<char> GetAlphaCharacters(AlphaCase alphaCase)
  {
    switch (alphaCase)
    {
      case AlphaCase.Lower:
        return LowerAlphaChars;

      case AlphaCase.Upper:
        return UpperAlphaChars;

      case AlphaCase.Both:
      default:
        return new List<char>(LowerAlphaChars.Concat(UpperAlphaChars));
    }
  }

  public IEnumerable<char> GenerateNumeric()
  {
    return Generate(NumericChars);
  }
  public IEnumerable<char> GenerateLowerAlpha()
  {
    return Generate(LowerAlphaChars);
  }
  public IEnumerable<char> GenerateUpperAlpha()
  {
    return Generate(UpperAlphaChars);
  }

  public IEnumerable<char> Generate(IList<char> characters)
  {
    if (characters == null)
    {
      throw new ArgumentNullException();
    }
    if (!characters.Any())
    {
      yield break;
    }

    while (true)
    {
      yield return characters[Rnd.Next(characters.Count)];
    }
  }
}

public enum AlphaCase
{
  Lower,
  Upper,
  Both
}

public interface IRandom
{
    int Next();
    int Next(int maxValue);
}

public sealed class SecureRandom : IRandom
{
    private readonly RandomNumberGenerator Rng = new RNGCryptoServiceProvider();

    public int Next()
    {
        var data = new byte[sizeof(int)];
        Rng.GetBytes(data);
        return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1);
    }

    public int Next(int maxValue)
    {
        return Next(0, maxValue);
    }

    public int Next(int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException();
        }
        return (int)Math.Floor(minValue + ((double)maxValue - minValue) * NextDouble());
    }

    public double NextDouble()
    {
        var data = new byte[sizeof(uint)];
        Rng.GetBytes(data);
        var randomUint = BitConverter.ToUInt32(data, 0);
        return randomUint / (uint.MaxValue + 1d);
    }
}

Edit: This is an answer to the question of how to generate random alphanumeric strings with conditions. As stated for testing purposes. Not to be used as is in a context where security is relevant.

编辑:这是如何生成带条件的随机字母数字字符串的问题的答案。如测试目的所述。不得在安全性相关的环境中使用。


Edit 2: Shamelessly borrowing from this answer, the solution now uses a wrapper around RNGCryptoServiceProvider. Still not sure if this should be used in security relevant context, but at least now it should be "better" than simply using Random

编辑2:从这个答案无耻地借鉴,解决方案现在使用RNGCryptoServiceProvider周围的包装器。仍然不确定是否应该在安全相关的上下文中使用它,但至少现在它应该比简单地使用Random更“好”