Как я могу генерировать случайные буквенно-цифровые строки?

737

Как я могу генерировать случайные 8-значные буквенно-цифровые строки в С#?

  • 2
    Какие ограничения у вас есть на набор символов? Просто символы английского языка и 0-9? Смешанный случай?
  • 8
    Или, возможно, для stackoverflow.com/questions/730268/… или stackoverflow.com/questions/1122483/c-random-string-generator
Показать ещё 5 комментариев
Теги:
random

31 ответ

1359
Лучший ответ

Я слышал, что LINQ является новым черным, поэтому здесь моя попытка использовать LINQ:

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

(Примечание. Использование класса Random делает это непригодным для любой безопасности, например, для создания паролей или токенов. Используйте класс RNGCryptoServiceProvider, если вам нужен сильный генератор случайных чисел.)

  • 0
    Нам нужен тест производительности на этом между прочим. Возможно при генерации 8000 вместо 8 символов.
  • 21
    @Alex: Я провел несколько быстрых тестов, и кажется, что при генерации более длинных строк он масштабируется в значительной степени линейно (при условии, что на самом деле доступно достаточно памяти). Сказав это, ответ Дэн Ригби был почти в два раза быстрее, чем этот в каждом тесте.
Показать ещё 13 комментариев
300
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();

for (int i = 0; i < stringChars.Length; i++)
{
    stringChars[i] = chars[random.Next(chars.Length)];
}

var finalString = new String(stringChars);

Не так элегантно, как решение Linq. (-:

(Примечание. Использование класса Random делает этот непригодным для любых связанных с безопасностью, таких как создание паролей или токенов. Используйте класс RNGCryptoServiceProvider, если вам нужен сильный генератор случайных чисел.)

  • 3
    @ Алекс: Это не самый быстрый ответ, но это самый быстрый «реальный» ответ (т. Е. Из тех, которые позволяют контролировать используемые символы и длину строки).
  • 1
    @Alex: решение GetRandomFileName Адама Порада GetRandomFileName быстрее, но не позволяет контролировать используемые символы, а максимально возможная длина составляет 11 символов. Решение Guid Дугласа молниеносно, но символы ограничены A-F0-9, а максимальная возможная длина составляет 32 символа.
Показать ещё 14 комментариев
258

Эта реализация (найденная через google) выглядит для меня звуком.

В отличие от некоторых представленных альтернатив, этот криптографически звучит.

using System.Security.Cryptography;
using System.Text;

namespace UniqueKey
{
    public class KeyGenerator
    {
        public static string GetUniqueKey(int maxSize)
        {
            char[] chars = new char[62];
            chars =
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[1];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetNonZeroBytes(data);
                data = new byte[maxSize];
                crypto.GetNonZeroBytes(data);
            }
            StringBuilder result = new StringBuilder(maxSize);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }
}

Выберите этот вариант из обсуждения альтернатив здесь

  • 10
    Это выглядит как правильный подход для меня - случайные пароли, соли, энтропия и т. Д. Не должны генерироваться с использованием Random (), который оптимизирован по скорости и генерирует воспроизводимые последовательности чисел; RNGCryptoServiceProvider.GetNonZeroBytes (), с другой стороны, создает дикие последовательности чисел, которые НЕ являются воспроизводимыми.
  • 19
    Буквы слегка смещены (255% 62! = 0). Несмотря на этот незначительный недостаток, это, безусловно, лучшее решение здесь.
Показать ещё 22 комментария
167

Решение 1 - самый большой "диапазон" с наиболее гибкой длиной

string get_unique_string(int string_length) {
    using(var rng = new RNGCryptoServiceProvider()) {
        var bit_count = (string_length * 6);
        var byte_count = ((bit_count + 7) / 8); // rounded up
        var bytes = new byte[byte_count];
        rng.GetBytes(bytes);
        return Convert.ToBase64String(bytes);
    }
}

Это решение имеет больший диапазон, чем использование GUID, поскольку GUID имеет пару фиксированных битов, которые всегда одинаковы и поэтому не являются случайными, например, 13 символов в шестнадцатеричном формате всегда "4" - по крайней мере, в GUID версии 6.

Это решение также позволяет генерировать строку любой длины.

Решение 2 - Одна строка кода - может содержать до 22 символов

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);

Вы не можете генерировать строки до тех пор, пока решение 1 и строка не имеют одинакового диапазона из-за фиксированных бит в GUID, но во многих случаях это будет выполнять эту работу.

Решение 3 - Немного меньше кода

Guid.NewGuid().ToString("n").Substring(0, 8);

В основном, это здесь для исторической цели. Он использует немного меньше кода, хотя это и связано с меньшим радиусом действия - поскольку он использует шестнадцатеричный вместо base64, для большего количества символов для представления того же диапазона сравниваются другие решения.

Это означает больше шансов на столкновение - тестирование его с 100 000 итераций из 8 символьных строк создало один дубликат.

  • 19
    Вы фактически сгенерировали дубликат? Удивительно 5,316,911,983,139,663,491,615,228,241,121,400,000 возможных комбинаций GUID.
  • 1
    (Вероятность этого равна 1,8807909613156600127499784595556e-27 или практически 0)
Показать ещё 9 комментариев
54

Вот пример, который я украл из примера Сэма Аллена в Dot Net Perls

Если вам нужно всего 8 символов, используйте Path.GetRandomFileName() в пространстве имен System.IO. Сэм говорит, что использование метода Path.GetRandomFileName иногда превосходит, потому что он использует RNGCryptoServiceProvider для лучшей случайности. Однако он ограничен 11 случайными символами.

GetRandomFileName всегда возвращает 12-значную строку с периодом 9-го символа. Поэтому вам нужно снять период (с тех пор как это не случайный), а затем взять 8 символов из строки. Фактически, вы могли бы просто взять первые 8 символов и не беспокоиться о периоде.

public string Get8CharacterRandomString()
{
    string path = Path.GetRandomFileName();
    path = path.Replace(".", ""); // Remove period.
    return path.Substring(0, 8);  // Return 8 character string
}

PS: спасибо Sam

  • 3
    Слишком умный на мой вкус. Я предпочитаю более простой подход.
  • 0
    Не будет работать при среднем доверии.
Показать ещё 11 комментариев
34

Основные цели моего кода:

  • Распределение строк почти равномерное (не заботятся о незначительных отклонениях, пока они малы)
  • Он выводит более нескольких миллиардов строк для каждого набора аргументов. Генерация 8-символьной строки (~ 47 бит энтропии) бессмысленна, если ваш PRNG генерирует только 2 миллиарда (31 бит энтропии) разных значений.
  • Это безопасно, поскольку я ожидаю, что люди будут использовать это для паролей или других токенов безопасности.

Первое свойство достигается за счет 64-битного значения по модулю размера алфавита. Для небольших алфавитов (например, из 62 символов из вопроса) это приводит к незначительному смещению. Второе и третье свойство достигаются с помощью RNGCryptoServiceProvider вместо System.Random.

using System;
using System.Security.Cryptography;

public static string GetRandomAlphanumericString(int length)
{
    const string alphanumericCharacters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789";
    return GetRandomString(length, alphanumericCharacters);
}

public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
    if (length < 0)
        throw new ArgumentException("length must not be negative", "length");
    if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
        throw new ArgumentException("length is too big", "length");
    if (characterSet == null)
        throw new ArgumentNullException("characterSet");
    var characterArray = characterSet.Distinct().ToArray();
    if (characterArray.Length == 0)
        throw new ArgumentException("characterSet must not be empty", "characterSet");

    var bytes = new byte[length * 8];
    new RNGCryptoServiceProvider().GetBytes(bytes);
    var result = new char[length];
    for (int i = 0; i < length; i++)
    {
        ulong value = BitConverter.ToUInt64(bytes, i * 8);
        result[i] = characterArray[value % (uint)characterArray.Length];
    }
    return new string(result);
}
  • 1
    Нет пересечения с 64 x Z и Math.Pow (2, Y). Таким образом, увеличение числа уменьшает предвзятость, но не устраняет ее. Я обновил свой ответ ниже, мой подход состоял в том, чтобы отбросить случайные входные данные и заменить их на другое значение.
  • 0
    @ Тодд Я знаю, что это не устраняет смещение, но я выбрал простоту этого решения вместо устранения практически несущественного смещения.
Показать ещё 5 комментариев
28

Самый простой:

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

Вы можете получить лучшую производительность, если вы жестко закодируете массив char и положитесь на System.Random:

public static string GetRandomAlphaNumeric()
{
    var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

Если вы беспокоитесь, что английские алфавиты могут когда-нибудь измениться, и вы можете потерять бизнес, тогда вы можете избежать жесткого кодирования, но должны работать немного хуже (сопоставимо с подходом Path.GetRandomFileName)

public static string GetRandomAlphaNumeric()
{
    var chars = 'a'.To('z').Concat('0'.To('9')).ToList();
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

public static IEnumerable<char> To(this char start, char end)
{
    if (end < start)
        throw new ArgumentOutOfRangeException("the end char should not be less than start char", innerException: null);
    return Enumerable.Range(start, end - start + 1).Select(i => (char)i);
}

Последние два подхода выглядят лучше, если вы можете сделать их методом расширения на экземпляре System.Random.

  • 1
    Использование chars.Select очень уродливо, так как оно основано на том, что выходной размер не превышает размера алфавита.
  • 0
    @CodesInChaos Я не уверен, что понимаю вас. Вы имеете в виду 'a'.To('z') ?
Показать ещё 3 комментария
18

Просто некоторые сравнения производительности различных ответов в этом потоке:

Способы и настройки

// what available
public static string possibleChars = "abcdefghijklmnopqrstuvwxyz";
// optimized (?) what available
public static char[] possibleCharsArray = possibleChars.ToCharArray();
// optimized (precalculated) count
public static int possibleCharsAvailable = possibleChars.Length;
// shared randomization thingy
public static Random random = new Random();


// http://stackoverflow.com/a/1344242/1037948
public string LinqIsTheNewBlack(int num) {
    return new string(
    Enumerable.Repeat(possibleCharsArray, num)
              .Select(s => s[random.Next(s.Length)])
              .ToArray());
}

// http://stackoverflow.com/a/1344258/1037948
public string ForLoop(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleCharsArray[random.Next(possibleCharsAvailable)];
    }
    return new string(result);
}

public string ForLoopNonOptimized(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleChars[random.Next(possibleChars.Length)];
    }
    return new string(result);
}

public string Repeat(int num) {
    return new string(new char[num].Select(o => possibleCharsArray[random.Next(possibleCharsAvailable)]).ToArray());
}

// http://stackoverflow.com/a/1518495/1037948
public string GenerateRandomString(int num) {
  var rBytes = new byte[num];
  random.NextBytes(rBytes);
  var rName = new char[num];
  while(num-- > 0)
    rName[num] = possibleCharsArray[rBytes[num] % possibleCharsAvailable];
  return new string(rName);
}

//SecureFastRandom - or SolidSwiftRandom
static string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; 
    char[] rName = new char[Length];
    SolidSwiftRandom.GetNextBytesWithMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

Результаты

Протестировано в LinqPad. Для размера строки 10, генерирует:

  • от Linq = chdgmevhcy [10]
  • from Loop = gtnoaryhxr [10]
  • from Select = rsndbztyby [10]
  • из GenerateRandomString = owyefjjakj [10]
  • из SecureFastRandom = VzougLYHYP [10]
  • из SecureFastRandom-NoCache = oVQXNGmO1S [10]

И показатели производительности имеют тенденцию меняться незначительно, очень редко NonOptimized на самом деле быстрее, а иногда ForLoop и GenerateRandomString переключают лидера.

  • LinqIsTheNewBlack (10000x) = 96762 тиков (9.6762 мс)
  • ForLoop (10000x) = 28970 тиков (2.897 мс)
  • ForLoopNonOptimized (10000x) = 33336 тиков прошло (3.3336 мс)
  • Повторение (10000x) = 78547 тиков (7.8547 мс)
  • GenerateRandomString (10000x) = 27416 тиков (2.7416 мс)
  • SecureFastRandom (10000x) = 13176 тиков, прошедших (5 мс) самый низкий [Различная машина]
  • SecureFastRandom-NoCache (10000x) = 39541 истекшее (17 мс) наименьшее [Различная машина]
  • 3
    Было бы интересно узнать, кто из них создал дупла.
  • 0
    @Junto - чтобы выяснить, что приводит к дубликатам, что-то вроде var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count()); где вы заменяете EachRandomizingMethod на ... каждый метод
Показать ещё 1 комментарий
16

Одна строка кода Membership.GeneratePassword() делает трюк :)

Вот демо для этого же.

8

Код, написанный Эриком Дж., довольно неряшлив (совершенно очевидно, что это с 6 лет назад... он, вероятно, не будет писать этот код сегодня), и есть даже некоторые проблемы.

В отличие от некоторых представленных альтернатив, этот криптографически звучит.

Неприемлемо... В пароле есть предвзятость (как написано в комментарии), bcdefgh немного более вероятны, чем другие (a не потому, что GetNonZeroBytes это isn ' t генерирует байты со значением 0, поэтому смещение для a уравновешивается им), поэтому оно не является криптографически звуковым.

Это должно исправить все проблемы.

public static string GetUniqueKey(int size = 6, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new RNGCryptoServiceProvider())
    {
        var data = new byte[size];

        // If chars.Length isn't a power of 2 then there is a bias if
        // we simply use the modulus operator. The first characters of
        // chars will be more probable than the last ones.

        // buffer used if we encounter an unusable random byte. We will
        // regenerate it in this buffer
        byte[] smallBuffer = null;

        // Maximum random number that can be used without introducing a
        // bias
        int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);

        crypto.GetBytes(data);

        var result = new char[size];

        for (int i = 0; i < size; i++)
        {
            byte v = data[i];

            while (v > maxRandom)
            {
                if (smallBuffer == null)
                {
                    smallBuffer = new byte[1];
                }

                crypto.GetBytes(smallBuffer);
                v = smallBuffer[0];
            }

            result[i] = chars[v % chars.Length];
        }

        return new string(result);
    }
}
6

Мы также используем произвольную строку random, но мы реализовали ее как вспомогательный элемент строки, поэтому она обеспечивает некоторую гибкость...

public static string Random(this string chars, int length = 8)
{
    var randomString = new StringBuilder();
    var random = new Random();

    for (int i = 0; i < length; i++)
        randomString.Append(chars[random.Next(chars.Length)]);

    return randomString.ToString();
}

Использование

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

или

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
5

Вопрос: Почему я должен тратить свое время, используя Enumerable.Range вместо ввода в "ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"?

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    public static void Main()
    {
        var randomCharacters = GetRandomCharacters(8, true);
        Console.WriteLine(new string(randomCharacters.ToArray()));
    }

    private static List<char> getAvailableRandomCharacters(bool includeLowerCase)
    {
        var integers = Enumerable.Empty<int>();
        integers = integers.Concat(Enumerable.Range('A', 26));
        integers = integers.Concat(Enumerable.Range('0', 10));

        if ( includeLowerCase )
            integers = integers.Concat(Enumerable.Range('a', 26));

        return integers.Select(i => (char)i).ToList();
    }

    public static IEnumerable<char> GetRandomCharacters(int count, bool includeLowerCase)
    {
        var characters = getAvailableRandomCharacters(includeLowerCase);
        var random = new Random();
        var result = Enumerable.Range(0, count)
            .Select(_ => characters[random.Next(characters.Count)]);

        return result;
    }
}

Ответ: Магические строки BAD. Кто-нибудь заметил, что в моей строке наверху не было "I"? Моя мать научила меня не использовать магические струны именно по этой причине...

n.b. 1: Как и многие другие, такие как @dtb, не используйте System.Random, если вам нужна криптографическая защита...

n.b. 2: Этот ответ не самый эффективный или самый короткий, но я хотел, чтобы пространство отделяло ответ от вопроса. Цель моего ответа - скорее предупредить против магических строк, чем дать фантастический инновационный ответ.

  • 0
    Почему меня волнует, что нет « I ?
  • 1
    Буквенно-цифровой (без учета регистра) - [A-Z0-9] . Если случайно ваша случайная строка только когда-либо покрывает [A-HJ-Z0-9] результат не охватывает весь допустимый диапазон, что может быть проблематично.
Показать ещё 2 комментария
5

Другим вариантом может быть использование Linq и объединение произвольных символов в строковый конструктор.

var chars = "abcdefghijklmnopqrstuvwxyz123456789".ToArray();
string pw = Enumerable.Range(0, passwordLength)
                      .Aggregate(
                          new StringBuilder(),
                          (sb, n) => sb.Append((chars[random.Next(chars.Length)])),
                          sb => sb.ToString());
5

Если вам просто нужен псевдослучайный буквенно-цифровой код, удобный для пользователя и полученный из целочисленного значения, я здесь предоставил решение:

Генерация псевдослучайных буквенно-цифровых значений

Преимущество состоит в том, что каждый генерируемый ключ гарантированно будет уникальным.

4

Мой простой однострочный код работает для меня :)

string  random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));

Response.Write(random.ToUpper());
Response.Write(random.ToLower());

Чтобы расширить эту строку для любой длины строки

    public static string RandomString(int length)
    {
        //length = length < 0 ? length * -1 : length;
        var str = "";

        do 
        {
            str += Guid.NewGuid().ToString().Replace("-", "");
        }

        while (length > str.Length);

        return str.Substring(0, length);
    }
  • 0
    Мне тоже нравится метод guid - он очень легкий
4

Несколько более чистая версия решения DTB.

    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    var random = new Random();
    var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
    return string.Join("", list);

Ваши настройки стиля могут отличаться.

  • 0
    Это намного лучше и эффективнее, чем принятый ответ.
4

Я искал более конкретный ответ, где я хочу контролировать формат случайной строки и наткнулся на этот пост. Например: номерные знаки (автомобилей) имеют определенный формат (по стране), и я хотел создать случайные номерные знаки.
Я решил написать собственный метод расширения Random для этого. (это нужно для повторного использования одного и того же объекта Random, так как вы могли бы удваивать сценарии многопоточности). Я создал gist (https://gist.github.com/SamVanhoutte/808845ca78b9c041e928), но также скопирует класс расширения здесь:

void Main()
{
    Random rnd = new Random();
    rnd.GetString("1-###-000").Dump();
}

public static class RandomExtensions
{
    public static string GetString(this Random random, string format)
    {
        // Based on http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c
        // Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)
        StringBuilder result = new StringBuilder();
        for(int formatIndex = 0; formatIndex < format.Length ; formatIndex++)
        {
            switch(format.ToUpper()[formatIndex])
            {
                case '0': result.Append(getRandomNumeric(random)); break;
                case '#': result.Append(getRandomCharacter(random)); break;
                default : result.Append(format[formatIndex]); break;
            }
        }
        return result.ToString();
    }

    private static char getRandomCharacter(Random random)
    {
        string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return chars[random.Next(chars.Length)];
    }

    private static char getRandomNumeric(Random random)
    {
        string nums = "0123456789";
        return nums[random.Next(nums.Length)];
    }
}
4

Ужасно, я знаю, но я просто не мог с собой поделать:


namespace ConsoleApplication2
{
    using System;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            Random adomRng = new Random();
            string rndString = string.Empty;
            char c;

            for (int i = 0; i < 8; i++)
            {
                while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]"));
                rndString += c;
            }

            Console.WriteLine(rndString + Environment.NewLine);
        }
    }
}

3
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }
3

Попробуйте объединить две части: уникальную (последовательность, счетчик или дату) и случайный

public class RandomStringGenerator
{
    public static string Gen()
    {
        return ConvertToBase(DateTime.UtcNow.ToFileTimeUtc()) + GenRandomStrings(5); //keep length fixed at least of one part
    }

    private static string GenRandomStrings(int strLen)
    {
        var result = string.Empty;

        var Gen = new RNGCryptoServiceProvider();
        var data = new byte[1];

        while (result.Length < strLen)
        {
            Gen.GetNonZeroBytes(data);
            int code = data[0];
            if (code > 48 && code < 57 || // 0-9
                code > 65 && code < 90 || // A-Z
                code > 97 && code < 122   // a-z
                )
            {
                result += Convert.ToChar(code);
            }
        }

        return result;
    }

    private static string ConvertToBase(long num, int nbase = 36)
    {
        var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish make algoritm more secure - change order of letter here

        // check if we can convert to another base
        if (nbase < 2 || nbase > chars.Length)
            return null;

        int r;
        var newNumber = string.Empty;

        // in r we have the offset of the char that was converted to the new base
        while (num >= nbase)
        {
            r = (int) (num % nbase);
            newNumber = chars[r] + newNumber;
            num = num / nbase;
        }
        // the last number to convert
        newNumber = chars[(int)num] + newNumber;

        return newNumber;
    }
}

Тесты:

[Test]
    public void Generator_Should_BeUnigue1()
    {
        //Given
        var loop = Enumerable.Range(0, 1000);
        //When
        var str = loop.Select(x=> RandomStringGenerator.Gen());
        //Then
        var distinct = str.Distinct();
        Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
    }
  • 0
    код и тест ... должен понравиться
  • 0
    1) Вы можете использовать символьные литералы вместо значений ASCII, связанных с этими символами. 2) В коде совпадения интервалов возникла ошибка «один за другим». Вам нужно использовать <= и >= вместо < и > . 3) Я бы добавил ненужные скобки вокруг выражений && чтобы было ясно, что они имеют приоритет, но, конечно, это только стилистический выбор.
Показать ещё 1 комментарий
3

После рассмотрения других ответов и рассмотрения комментариев CodeInChaos вместе с CodeInChaos по-прежнему предвзятым (хотя и менее) ответом, я подумал, что требуется окончательное окончательное решение для вырезания и вставки. Поэтому, обновляя свой ответ, я решил изо всех сил.

Для обновленной версии этого кода посетите новый репозиторий Hg на Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom. Я рекомендую вам скопировать и вставить код из: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (убедитесь, что вы нажимаете кнопку Raw, чтобы упростить ее копирование и убедиться у вас есть последняя версия, я думаю, что эта ссылка относится к конкретной версии кода, а не к последнему).

Обновленные примечания:

  • Относительно некоторых других ответов. Если вы знаете длину вывода, вам не нужен StringBuilder, и при использовании ToCharArray это создает и заполняет массив (вам не нужно сначала создавать пустой массив)
  • Относительно некоторых других ответов - вы должны использовать NextBytes, а не получать по одному за производительность
  • Технически вы можете привязать массив байтов для более быстрого доступа. Обычно это стоит того, когда вы повторяете более 6-8 раз по массиву байтов. (Не здесь)
  • Использование RNGCryptoServiceProvider для лучшей случайности
  • Использование кэширования буфера 1 МБ случайных данных - сравнение производительности кэшированных данных с одним байтом составляет ~ 1000 раз быстрее - с 9 мс по сравнению с 1 МБ против 989 мс для неактивных.
  • Оптимизированное отклонение зоны смещения в моем новом классе.

Окончательное решение вопроса:

static char[] charSet =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
    char[] rName = new char[Length];
    SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

Но вам нужен мой новый (непроверенный) класс:

/// <summary>
/// My benchmarking showed that for RNGCryptoServiceProvider:
/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference 
/// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)
/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached
/// </summary>
class SecureFastRandom
{
    static byte[] byteCache = new byte[1000000]; //My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)
    static int lastPosition = 0;
    static int remaining = 0;

    /// <summary>
    /// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function
    /// </summary>
    /// <param name="buffer"></param>
    public static void DirectGetBytes(byte[] buffer)
    {
        using (var r = new RNGCryptoServiceProvider())
        {
            r.GetBytes(buffer);
        }
    }

    /// <summary>
    /// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance
    /// </summary>
    /// <param name="buffer"></param>
    public static void GetBytes(byte[] buffer)
    {
        if (buffer.Length > byteCache.Length)
        {
            DirectGetBytes(buffer);
            return;
        }

        lock (byteCache)
        {
            if (buffer.Length > remaining)
            {
                DirectGetBytes(byteCache);
                lastPosition = 0;
                remaining = byteCache.Length;
            }

            Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
            lastPosition += buffer.Length;
            remaining -= buffer.Length;
        }
    }

    /// <summary>
    /// Return a single byte from the cache of random data.
    /// </summary>
    /// <returns></returns>
    public static byte GetByte()
    {
        lock (byteCache)
        {
            return UnsafeGetByte();
        }
    }

    /// <summary>
    /// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache.
    /// </summary>
    /// <returns></returns>
    static byte UnsafeGetByte()
    {
        if (1 > remaining)
        {
            DirectGetBytes(byteCache);
            lastPosition = 0;
            remaining = byteCache.Length;
        }

        lastPosition++;
        remaining--;
        return byteCache[lastPosition - 1];
    }

    /// <summary>
    /// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    public static void GetBytesWithMax(byte[] buffer, byte max)
    {
        if (buffer.Length > byteCache.Length / 2) //No point caching for larger sizes
        {
            DirectGetBytes(buffer);

            lock (byteCache)
            {
                UnsafeCheckBytesMax(buffer, max);
            }
        }
        else
        {
            lock (byteCache)
            {
                if (buffer.Length > remaining) //Recache if not enough remaining, discarding remaining - too much work to join two blocks
                    DirectGetBytes(byteCache);

                Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
                lastPosition += buffer.Length;
                remaining -= buffer.Length;

                UnsafeCheckBytesMax(buffer, max);
            }
        }
    }

    /// <summary>
    /// Checks buffer for bytes equal and above max. Must be called within lock of byteCache.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    static void UnsafeCheckBytesMax(byte[] buffer, byte max)
    {
        for (int i = 0; i < buffer.Length; i++)
        {
            while (buffer[i] >= max)
                buffer[i] = UnsafeGetByte(); //Replace all bytes which are equal or above max
        }
    }
}

Для истории - мое старое решение для этого ответа, используется Случайный объект:

    private static char[] charSet =
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();

    static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
    static int byteSize = 256; //Labelling convenience
    static int biasZone = byteSize - (byteSize % charSet.Length);
    static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
    public string GenerateRandomString(int Length) //Configurable output string length
    {
      byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
      char[] rName = new char[Length];
      lock (rGen) //~20-50ns
      {
          rGen.NextBytes(rBytes);

          for (int i = 0; i < Length; i++)
          {
              while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there still a bias on index 0.
                  rBytes[i] = rGen.NextByte();
              rName[i] = charSet[rBytes[i] % charSet.Length];
          }
      }
      return new string(rName);
    }

Производительность:

  • SecureFastRandom. Первый сингл = ~ 9-33мс. Незаметный. Текущая: 5 мс (иногда до 13 мс) более 10000 итераций, с одной средней итерацией = 1,5 микросекунды.. Примечание. Требуется в целом 2, но иногда до 8 обновлений кеша - зависит от того, сколько одиночных байтов превышает зону смещения.
  • Случайный. Первый сингл = ~ 0-1ms. Незаметный. Текущая: 5 мсек более 10000 итераций. С одной средней итерацией = 0,5 микросекунды.. О той же скорости.
  • 0
    Я обнаружил некоторые незначительные улучшения производительности вашего метода, которые, похоже, были излишними - stackoverflow.com/a/17092645/1037948
  • 5
    1) Почему все эти магические константы? Вы указали длину вывода три раза. Просто определите его как константу или параметр. Вы можете использовать charSet.Length вместо 62 . 2) Статический Random случай без блокировки означает, что этот код не является потокобезопасным. 3) уменьшение 0-255 мод 62 вводит обнаруживаемое смещение. 4) Вы не можете использовать ToString для массива char, который всегда возвращает "System.Char[]" . Вам нужно использовать new String(rName) вместо этого.
Показать ещё 8 комментариев
2

Вот вариант решения Eric J, то есть криптографически звуковой, для WinRT (приложение для Windows Store):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new StringBuilder(length);
    for (int i = 0; i < length; ++i)
    {
        result.Append(CryptographicBuffer.GenerateRandomNumber() % chars.Length);
    }
    return result.ToString();
}

Если производительность имеет значение (особенно когда длина высокая):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new System.Text.StringBuilder(length);
    var bytes = CryptographicBuffer.GenerateRandom((uint)length * 4).ToArray();
    for (int i = 0; i < bytes.Length; i += 4)
    {
        result.Append(BitConverter.ToUInt32(bytes, i) % chars.Length);
    }
    return result.ToString();
}
  • 1
    Это не криптографически обоснованно. Существует небольшое смещение из-за того, что операция модуля не распределяет всю ширину Улонга в равной степени на 62 символа.
2

Решение без использования Random:

var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());
  • 2
    NewGuid использует случайное внутреннее. Так что это все еще использует случайное, это просто скрывает это.
2

Теперь в аромате с одним лайнером.

private string RandomName
    {
        get
        {
            return new string(
                Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                    .Select(s =>
                    {
                        var cryptoResult = new byte[4];
                        new RNGCryptoServiceProvider().GetBytes(cryptoResult);
                        return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                    })
                    .ToArray());
        }
    }
  • 1
    Использование свойства для чего-то, что изменяется при каждом доступе, довольно сомнительно. Я бы рекомендовал вместо этого использовать метод.
  • 1
    RNGCryptoServiceProvider следует утилизировать после использования.
Показать ещё 1 комментарий
1

Я не знаю, насколько это криптографически звучит, но это более читаемо и лаконично, чем более сложные решения (imo), и это должно быть более "случайным", чем решения на основе System.Random.

return alphabet
    .OrderBy(c => Guid.NewGuid())
    .Take(strLength)
    .Aggregate(
        new StringBuilder(),
        (builder, c) => builder.Append(c))
    .ToString();

Я не могу решить, думаю ли я, что эта версия или следующая "красивее", но они дают точные результаты:

return new string(alphabet
    .OrderBy(o => Guid.NewGuid())
    .Take(strLength)
    .ToArray());

Конечно, он не оптимизирован для скорости, поэтому, если он критически настроен для генерации миллионов случайных строк каждую секунду, попробуйте еще один!

ПРИМЕЧАНИЕ. Это решение не допускает повторений символов в алфавите, а алфавит ДОЛЖЕН быть равным или большим размером, чем выходная строка, делая этот подход менее желательным в некоторых случаях, все зависит от вашего использования, случай.

1

Я знаю, что это не лучший способ. Но вы можете попробовать это.

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
  • 0
    Лучший ONE-LINE подход!
  • 2
    Как эта линия? Console.WriteLine ($ "Случайная строка: {Path.GetRandomFileName (). Replace (". "," ")}"); это одна строка.
0

Вот механизм генерации случайной буквенно-цифровой строки (я использую это для генерации паролей и тестовых данных) без указания алфавита и цифр,

CleanupBase64 удалит необходимые фрагменты в строке и продолжит добавлять случайные буквенно-цифровые буквы рекурсивно.

        public static string GenerateRandomString(int length)
        {
            var numArray = new byte[length];
            new RNGCryptoServiceProvider().GetBytes(numArray);
            return CleanUpBase64String(Convert.ToBase64String(numArray), length);
        }

        private static string CleanUpBase64String(string input, int maxLength)
        {
            input = input.Replace("-", "");
            input = input.Replace("=", "");
            input = input.Replace("/", "");
            input = input.Replace("+", "");
            input = input.Replace(" ", "");
            while (input.Length < maxLength)
                input = input + GenerateRandomString(maxLength);
            return input.Length <= maxLength ?
                input.ToUpper() : //In my case I want capital letters
                input.ToUpper().Substring(0, maxLength);
        }
  • 0
    Вы объявили GenerateRandomString и сделали вызов GetRandomString изнутри SanitiseBase64String . Также вы объявили SanitiseBase64String и CleanUpBase64String в GenerateRandomString .
  • 1
    Спасибо, я исправил это.
0
public static class StringHelper
{
    private static readonly Random random = new Random();

    private const int randomSymbolsDefaultCount = 8;
    private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private static int randomSymbolsIndex = 0;

    public static string GetRandomSymbols()
    {
        return GetRandomSymbols(randomSymbolsDefaultCount);
    }

    public static string GetRandomSymbols(int count)
    {
        var index = randomSymbolsIndex;
        var result = new string(
            Enumerable.Repeat(availableChars, count)
                      .Select(s => {
                          index += random.Next(s.Length);
                          if (index >= s.Length)
                              index -= s.Length;
                          return s[index];
                      })
                      .ToArray());
        randomSymbolsIndex = index;
        return result;
    }
}
  • 2
    1) статические методы должны быть потокобезопасными. 2) Какой смысл увеличивать индекс, а не использовать результат random.Next напрямую? Усложняет код и не дает ничего полезного.
0

Если ваши значения не являются полностью случайными, но на самом деле могут зависеть от чего-то, вы можете вычислить хеш-память md5 или sha1 этого "somwthing", а затем усечь его до любой длины, которую вы хотите.

Также вы можете сгенерировать и обрезать указатель.

-2

Очень простое решение. Он использует ASCII значения и просто генерирует "случайные" символы между ними.

public static class UsernameTools
{
    public static string GenerateRandomUsername(int length = 10)
    {
        Random random = new Random();
        StringBuilder sbuilder = new StringBuilder();
        for (int x = 0; x < length; ++x)
        {
            sbuilder.Append((char)random.Next(33, 126));
        }
        return sbuilder.ToString();
    }

}
-4

Вы просто используете сборку SRVTextToImage. И напишите ниже код для генерации случайной строки.

CaptchaRandomImage c1 = new CaptchaRandomImage();
            string text = c1.GetRandomString(8);

В основном он используется для реализации Captcha. Но в вашем случае это также работает. Надеюсь, что это поможет.

  • 0
    То есть, вы говорите, что мы должны использовать какую-то стороннюю библиотеку, изначально разработанную для генерации CAPTCHA для генерации случайной строки?
  • 3
    Строка не единственное, что случайно в этом предложении! я люблю это, определенно самый оригинальный!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню