首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >随机字符串生成

随机字符串生成
EN

Code Review用户
提问于 2011-11-12 09:37:18
回答 10查看 87.5K关注 0票数 32

我使用这个C#函数来生成一个系统的随机优惠券。我该如何改进呢?

代码语言:javascript
复制
public static string GenerateCoupon(int length)
{
    string result = string.Empty;
    Random random = new Random((int)DateTime.Now.Ticks);
    List<string> characters = new List<string>() { };
    for (int i = 48; i < 58; i++)
    {
        characters.Add(((char)i).ToString());
    }
    for (int i = 65; i < 91; i++)
    {
        characters.Add(((char)i).ToString());
    }
    for (int i = 97; i < 123; i++)
    {
        characters.Add(((char)i).ToString());
    }
    for (int i = 0; i < length; i++)
    {
        result += characters[random.Next(0, characters.Count)];
        Thread.Sleep(1);
    }
    return result;
}

业务要求是:

  1. 优惠券的长度不是固定的和静态的。
  2. 优惠券可以包含and、and和0-9 (区分大小写的字母数字)。
  3. 优惠券应该是唯一的(这意味着我们将它们作为密钥存储在数据库中的表中,对于每一张优惠券,我们检查其唯一性)
EN

回答 10

Code Review用户

发布于 2011-11-12 22:21:10

让我们看看其中的一些代码:

代码语言:javascript
复制
Random random = new Random((int)DateTime.Now.Ticks);

您不需要从时钟中为随机构造函数创建种子,无参数构造函数可以这样做:

代码语言:javascript
复制
Random random = new Random();
代码语言:javascript
复制
List<string> characters = new List<string>() { };

如果没有在列表中添加任何项目,则不需要初始化括号:

代码语言:javascript
复制
List<string> characters = new List<string>();
代码语言:javascript
复制
result += characters[random.Next(0, characters.Count)];

使用+=连接字符串是错误的做法。字符串不追加在末尾,因为字符串是不可变的。像x += y;这样的代码实际上以x = String.Concat(x, y)结束。您应该使用StringBuilder来构建字符串。

代码语言:javascript
复制
Thread.Sleep(1);

你到底为什么睡在圈中间?

与其创建字符串列表,不如使用字符串文本从以下位置选择字符:

代码语言:javascript
复制
public static string GenerateCoupon(int length) {
  Random random = new Random();
  string characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  StringBuilder result = new StringBuilder(length);
  for (int i = 0; i < length; i++) {
    result.Append(characters[random.Next(characters.Length)]);
  }
  return result.ToString();
}

考虑是否应该包括所有这些字符,或者忽略类似的字符,如oO0。使用字符串文字中的字符可以很容易地做到这一点。

编辑:

如果要多次调用该方法,则应将随机生成器作为参数发送:

代码语言:javascript
复制
public static string GenerateCoupon(int length, Random random) {
  string characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  StringBuilder result = new StringBuilder(length);
  for (int i = 0; i < length; i++) {
    result.Append(characters[random.Next(characters.Length)]);
  }
  return result.ToString();
}

用法:

代码语言:javascript
复制
Random rnd = new Random();
string[] coupon = new string[10];
for (int i = 0; i < coupon.Length; i++) {
  coupon[i] = GenerateCoupon(10, rnd);
}
Console.WriteLine(String.Join(Environment.NewLine,coupon));

示例输出:

代码语言:javascript
复制
LHUSer9dPZ
btK0S01yLb
hruw4IXINJ
hwMdRDRujt
cr4TDezvcZ
b8tVETNXNL
JrG6sfXgZF
Y7FRypnRiQ
JbfnhY3qOx
quWNakbybY
票数 33
EN

Code Review用户

发布于 2011-11-14 00:00:15

您不应该使用Random来生成优惠券。您的优惠券在某种程度上是可以预测的:如果有人可以看到一些优惠券(特别是几张连续的优惠券),他们将能够重建种子并生成所有的优惠券。对于大多数数字模拟和一些游戏来说,Random是可以的,当您需要生成不可预测的值时,这一点也不太好。你的优惠券就像密码,你需要密码质量的随机性。幸运的是,C#库中有一个密码质量的随机生成器:System.Security.Cryptography.RNGCryptoServiceProvider

这个RNG返回字节。一个字节中有256个可能的值。您的优惠券只能使用62个字符中的一个,因此您需要拒绝不映射到ASCII字母或数字的字节。

此外,在逐块构建字符串时,您应该使用使用StringBuilder。当您完成构建它时,将其解析为字符串。

代码语言:javascript
复制
var couponLength = 32;
StringBuilder coupon = new StringBuilder(couponLength);
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] rnd = new byte[1];
int n = 0;
while (n < couponLength) {
    rng.GetBytes(rnd);
    char c = (char)rnd[0];
    if ((Char.IsDigit(c) || Char.IsLetter(c)) && rnd[0] < 127) {
        ++n;
        coupon.Append(c);
    }
}
return coupon.ToString();

通过拒绝较少的值,您可以使生成速度提高大约4倍。不要只接受映射到您想要的字符的62个值,除以4得到64个等概率值中的一个,并接受其中的62个(将它们映射到正确的字符)并拒绝2。

代码语言:javascript
复制
while (n < couponLength) {
    rng.GetBytes(rnd);
    rnd[0] %= 64;
    if (rnd[0] < 62) {
        ++n;
        coupon.Append((char)((rnd[0] <= 9 ? '0' : rnd[0] <= 35 ? 'A' - 10 : 'a' - 36) + rnd[0]);
    }
}
票数 23
EN

Code Review用户

发布于 2011-11-12 13:04:33

一些一般性的想法,我希望所有的工作在C#也。如果答案不是正确的C#语法,请随意编辑它。

1,将characters列表的类型更改为char,并将循环变量也更改为char。这样您就不需要强制转换了,for循环更容易读懂:

代码语言:javascript
复制
List<char> characters = new List<char>() { };
for (char c = '0'; i <= '9'; c++) {
    characters.Add(c);
}
...
for (int i = 0; i < length; i++){
    result += characters[random.Next(0, characters.Count)];
}

2、是否存在Thread.Sleep(1);的原因?看起来没必要。

我要把0Ool1从名单上删除。很容易把它们混在一起。

4 .我会提取一个AllowedCharacters方法:

代码语言:javascript
复制
public static List<char> AllowedCharacters() {
    List<char> characters = new List<char>() { };
    for (char c = '0'; i <= '9'; c++) {
        characters.Add(c);
    }
    ...
    characters.Remove('O');
    characters.Remove('0');
    ...
    return characters;
}

public static string GenerateCoupon(int length)
{
    string result = string.Empty;
    Random random = new Random((int)DateTime.Now.Ticks);
    List<string> characters = AllowedCharacters();

    for (int i = 0; i < length; i++) {
        result += characters[random.Next(0, characters.Count)];
    }
    return result;
}
票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/5983

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档