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

时间:2017-05-17 12:41:10

标签: c# .net regex

根据以下代码,我需要根据方法输入在不同情况下生成一个字符串。我的问题是我想要生成 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 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:2)

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

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;
            }
        }
    }
}

现在,由于实现迭代器方法的时髦猴子状态机,尽管有while (true),此代码仍处理RNG。

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

答案 2 :(得分:-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);

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

答案 3 :(得分:-2)

OverView 以下代码使用了Regex.Replace IsMatchXXXXXX允许用户创建任意长度模式,例如XX-XXX将用随机字符或字母替换任何XX-XX

例如,A(-1d可以返回XXXX或仅使用短划线A(1d返回Web.Security.Membership.GeneratePassword

安全 它使用// 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)); 来获取48个随机字符,数字和不包含的字符串。然后根据所需的模式从该随机字符中剔除一个明确的密码。

  

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

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

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

这个得到了

if opts.fields:
    fields = ", ".join([x.strip() for x in opts.fields.split(",")])
else:
    fields = ", ".join(rows.next())

qry = "INSERT INTO %s (%s) VALUES (%s);" % (table,
                                            fields,
                                            ",".join("?"*len(rows)),)

此答案基于我博客上的非安全实施,请参阅C#: Generate a Random Sequence of Numbers and Letters From a User Defined Pattern and Characters

答案 4 :(得分:-3)

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

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);
    }
}

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

编辑2:无耻地从this answer借用,解决方案现在使用RNGCryptoServiceProvider周围的包装器。仍不确定这是否应该用于安全相关的上下文中,但至少现在它应该比仅使用Random

更“好”