简体   繁体   English

如何使用C#生成8个字符,900000个上字母/数字唯一键?

[英]How to generate 8 characters, 900000 Upper alpha/numeric unique keys with C#?

I'm trying to generate 900,000 keys and store them to a db. 我正在尝试生成900,000个密钥并将其存储到数据库。

Each value has to be 8 chars and different from generated ones. 每个值必须是8个字符,并且与生成的字符不同。

Here is my code look like, it's doing ok but it's too slow... 这是我的代码,看起来不错,但速度太慢...

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        richTextBox1.Visible = false;
        Generate();
    }

    public int num = 0;

    private void Den1()
    {
        for (int i = 0; i < 900000; i++)
        {
            string randomStr = RandomString(8);
            string textToSearchFor = randomStr;

            int index = richTextBox1.Text.IndexOf(textToSearchFor, StringComparison.OrdinalIgnoreCase);

            if (index >= 0)
            {
                MessageBox.Show("Duplicate ignored from 1!");
                i--;
            }
            else
            {
                richTextBox1.Text += randomStr + "\n";
                num++;
            }

            label1.Text = "" + num;
        }

        richTextBox1.Visible = true;
    }

    public void Generate()
    {
        CheckForIllegalCrossThreadCalls = false;
        Thread thread1 = new Thread(Deneme1);

        thread1.Start();
    }

    private string RandomString(int length, string allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    {
        if (length < 0) throw new ArgumentOutOfRangeException("length", "length cannot be less than zero.");
        if (string.IsNullOrEmpty(allowedChars)) throw new ArgumentException("allowedChars may not be empty.");

        const int byteSize = 0x100;
        var allowedCharSet = new HashSet<char>(allowedChars).ToArray();
        if (byteSize < allowedCharSet.Length) throw new ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize));

        // Guid.NewGuid and System.Random are not particularly random. By using a
        // cryptographically-secure random number generator, the caller is always
        // protected, regardless of use.
        using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
        {
            var result = new StringBuilder();
            var buf = new byte[128];
            while (result.Length < length)
            {
                rng.GetBytes(buf);
                for (var i = 0; i < buf.Length && result.Length < length; ++i)
                {
                    // Divide the byte into allowedCharSet-sized groups. If the
                    // random value falls into the last group and the last group is
                    // too small to choose from the entire allowedCharSet, ignore
                    // the value in order to avoid biasing the result.
                    var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);
                    if (outOfRangeStart <= buf[i]) continue;
                    result.Append(allowedCharSet[buf[i] % allowedCharSet.Length]);
                }
            }
            return result.ToString();
        }
    }
}

Well to start with, I would generate them all and then put them into the UI. 首先,我将全部生成它们, 然后将它们放到UI中。 I also wouldn't use string.IndexOf to find if you've already got a match. 我也不会使用string.IndexOf来查找是否已经匹配。 Use a HashSet<string> : 使用HashSet<string>

HashSet<string> codes = new HashSet<string>();
while (codes.Count < 900000)
{
    codes.Add(RandomString(8));  
}
// Then do what you want with the codes

You also shouldn't be setting CheckForIllegalCrossThreadCalls to false. 您也不应该CheckForIllegalCrossThreadCalls设置为false。 Just generate them all in the background and then use Invoke to get to the UI thread to update your UI. 只需在后台全部生成它们,然后使用Invoke进入UI线程即可更新您的UI。 If you really want to update as you go, you could do so periodically... although I wouldn't put them all into a single text box. 如果您确实想随时进行更新,则可以定期进行更新……尽管我不会将它们全部放在一个文本框中。

Next, I'd look at the RandomString method. 接下来,我看一下RandomString方法。 Do you really need to create a new RNGCryptoServiceProvider on every iteration? 您是否真的需要在每次迭代中创建一个新的RNGCryptoServiceProvider Why not create one and then reuse it? 为什么不创建一个然后再使用呢?

Likewise, why bother creating a new HashSet<char> and calling ToArray on every call ? 同样,为什么还要麻烦创建一个新的HashSet<char>并在每次调用调用ToArray呢? It sounds like you should probably have a separate class as a RandomCodeGenerator which remembers: 听起来您可能应该像RandomCodeGenerator那样拥有一个单独的类, RandomCodeGenerator记住:

  • The size of code you're generating 您生成的代码大小
  • The random number generator 随机数发生器
  • The allowed characters 允许的字符

That will let you get away from doing a lot of setup work on every iteration. 这样一来,您就无需在每次迭代中进行大量设置工作。

I'd then personally create a char[] of the right size and just iterate until you've filled it, then create a string from that... I don't see the need for StringBuilder ... but that's probably not hurting you. 然后,我将亲自创建一个大小合适的char[]并进行迭代,直到将其填满为止,然后从中创建一个字符串...我看不到需要StringBuilder ...,但这可能不会有任何伤害您。

You could use a Guid to quickly generate your keys: 您可以使用Guid快速生成密钥:

Guid guid = Guid.NewGuid();

string theString = guid.ToString().Replace("-", string.Empty).Substring(0, 8).ToUpperInvariant();

You could do that directly in SQL, like: https://stackoverflow.com/a/9631898/403335 您可以直接在SQL中执行此操作,例如: https : //stackoverflow.com/a/9631898/403335

But even if you do that with code, I would fill a container with a sequence of all the possible values. 但是,即使您使用代码执行此操作,我也会在容器中填充所有可能值的序列。

Then randomly select elements from the sequence container, use the element and delete it from the container. 然后从序列容器中随机选择元素,使用该元素并将其从容器中删除。 This way you will surely get all the numbers, without wasting time in finding a "hole" number (not already picked up). 这样,您肯定会得到所有数字,而不会浪费时间寻找“空”数字(尚未拾取)。

In code: 在代码中:

    Random random = new Random();
    List<string> uniqueStrings = new List<string>(900000);
    List<string> randomString = new List<string>(900000);

    for (char c1 = 'A'; c1 <= 'Z'; ++c1) 
        for (char c2 = 'A'; c2 <= 'Z'; ++c2) 
            for (char c3 = 'A'; c3 <= 'Z'; ++c3) 
                for (char c4 = 'A'; c4 <= 'Z'; ++c4) 
                    for (char c5 = 'A'; c5 <= 'Z'; ++c5) 
                        for (char c6 = 'A'; c6 <= 'Z'; ++c6) 
                            for (char c7 = 'A'; c7 <= 'Z'; ++c7) 
                                for (char c8 = 'A'; c8 <= 'Z'; ++c8) 
                                    uniqueStrings.Add(string.Format("{0}{1}{2}{3}{4}{5}{6}{7}", c1, c2, c3, c4, c5, c6, c7, c8));

    for (int i = 0; i < uniqueStrings.Count; i++)
    {
        const int index = random.Next(uniqueStrings.Count) + 1;
        randomString.Add(uniqueStrings[index]);
        uniqueStrings.RemoveAt(index);
    }

I think this code may be helpful 我认为这段代码可能会有所帮助

public static string GeneratePassword()
{
    string strPwdchar = "abcdefghijklmnopqrstuvwxyz0123456789#+@&$ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    string strPwd = "";
    Random rnd = new Random();
    for (int i = 0; i <= 7; i++)
    {
        int iRandom = rnd.Next(5, strPwdchar.Length - 1);
        strPwd += strPwdchar.Substring(iRandom, 1);
    }
    return strPwd;
}

Iterate it 90,000 times on a mouse click event.....I hope things will work 在鼠标单击事件上将其迭代90,000次.....我希望一切正常

Have a look at this discussion. 看看这个讨论。
My preferred solution, which I use for generating +100.000 unique strings is the linked answer 我首选的用于生成+100.000唯一字符串的解决方案是链接的答案

First of all RichTextBox is not an appropriate value store. 首先,RichTextBox不是适当的值存储。 The IndexOf method of the TextBox is most likely the slowest part of your code you need to replace it with something else, I would rather use a HashSet<string> to figure out of there are duplicates. TextBoxIndexOf方法很可能是代码中最慢的部分,需要用其他东西替换它,我宁愿使用HashSet<string>来找出重复项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM