Як я можу генерувати випадкові буквено-цифрові рядки?


966

Як я можу генерувати випадкову буквено-цифрову рядок у 8 символів у C #?


2
Які обмеження, якщо у вас є набір символів? Просто символи англійської мови та 0-9? Змішаний чохол?
Ерік Дж.


9
Зауважте, що НЕ слід використовувати жоден метод на основі Randomкласу для генерації паролів. Насіннєвий матеріал Randomмає дуже низьку ентропію, тому це не дуже безпечно. Використовуйте криптографічний PRNG для паролів.
CodesInChaos

2
Було б добре включити в це питання локалізацію мови. Особливо, якщо вашій гуї потрібно обслуговувати китайську чи болгарську!
Пітер Джемсменсон

15
Щось із такою кількістю опублікованих результатів, і ця кількість якісних відповідей не заслуговує на те, щоб бути позначеними як закриті. Я голосую за його відновлення.
Джон Коулман

Відповіді:


1684

Я чув, що 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клас, якщо вам потрібен сильний генератор випадкових чисел.)


23
@ Алекс: Я провів кілька швидких тестів, і, здається, масштабування значно лінійно під час генерації довших рядків (до тих пір, поки фактично достатньо пам’яті). Сказавши це, відповідь Дена Рігбі була майже вдвічі швидшою, ніж ця у кожному тесті.
ЛукаH

6
Добре. Якщо вашими критеріями є те, що він використовує linq і що в ньому є хитра розповідь про код, то це, безумовно, коліна бджоли. Як кодова розповідь, так і фактичний шлях виконання є досить неефективним та непрямим. Не зрозумійте мене неправильно, я величезний хіпстер коду (я люблю пітон), але це в значній мірі машина рубея Голдберга.
eremzeit

5
Хоча це технічно відповідає на питання, його висновок дуже вводить в оману. Створення 8 випадкових символів здається, що результатів може бути дуже багато, тоді як це в кращому випадку дає 2 мільярди різних результатів. А на практиці ще менше. Ви також повинні додати BIG FAT попередження, щоб не використовувати це для будь-яких речей, пов’язаних із безпекою.
CodesInChaos

41
@xaisoft: Малі літери залишаються як вправа для читача.
dtb

15
Наступний рядок ефективніше пам'яті (і, таким чином, часу), ніж данийreturn new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
Тайсон Вільямс

375
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клас, якщо вам потрібен сильний генератор випадкових чисел.)


4
@Alex: Це не абсолютна швидка відповідь, але це найшвидша "реальна" відповідь (тобто з тих, які дозволяють контролювати використовувані символи та довжину рядка).
ЛукаH

2
@ Алекс: Рішення Адама Порада GetRandomFileNameшвидше, але не дозволяє контролювати використовувані символи, а максимальна можлива довжина - 11 символів. GuidРішення Дугласа блискавично, але символи обмежені A-F0-9, а максимальна можлива довжина - 32 символи.
ЛукаH

1
@Adam: Так, ви можете домогтися результату декількох дзвінків, GetRandomFileNameале тоді (a) ви втратите свою перевагу в роботі та (b) ваш код ускладниться.
ЛукаХ

1
@xaisoft створіть свій примірник об'єкта Random () поза вашим циклом. Якщо ви створите безліч екземплярів Random () за короткий проміжок часу, тоді виклик .Next () поверне те саме значення, що і Random () використовує часове насіння.
Дан Рігбі

2
@xaisoft Не використовуйте цю відповідь для нічого важливого для безпеки, як-от паролів. System.Randomне підходить для безпеки.
CodesInChaos

333

ОНОВЛЕНО на основі коментарів. Початкова реалізація генерувала ah ~ 1,95% часу, а решта символів ~ 1,56% часу. Оновлення генерує всі символи ~ 1,61% часу.

ПОДДЕРЖКА FRAMEWORK - .NET Core 3 (і майбутні платформи, які підтримують .NET Standard 2.1 або вище) забезпечує криптографічно звуковий метод RandomNumberGenerator.GetInt32 () для генерації випадкового цілого числа в бажаному діапазоні.

На відміну від деяких представлених альтернатив, ця криптографічно звучить .

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

namespace UniqueKey
{
    public class KeyGenerator
    {
        internal static readonly char[] chars =
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); 

        public static string GetUniqueKey(int size)
        {            
            byte[] data = new byte[4*size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            for (int i = 0; i < size; i++)
            {
                var rnd = BitConverter.ToUInt32(data, i * 4);
                var idx = rnd % chars.Length;

                result.Append(chars[idx]);
            }

            return result.ToString();
        }

        public static string GetUniqueKeyOriginal_BIASED(int size)
        {
            char[] chars =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }
}

На основі обговорення альтернатив тут та оновлених / модифікованих на основі коментарів нижче.

Ось невеликий тестовий ремінь, який демонструє розподіл символів у старому та оновленому виході. Для глибокого обговорення аналізу випадковості ознайомтеся на random.org.

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

namespace CryptoRNGDemo
{
    class Program
    {

        const int REPETITIONS = 1000000;
        const int KEY_SIZE = 32;

        static void Main(string[] args)
        {
            Console.WriteLine("Original BIASED implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);

            Console.WriteLine("Updated implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
            Console.ReadKey();
        }

        static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
        {
            Dictionary<char, int> counts = new Dictionary<char, int>();
            foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);

            for (int i = 0; i < REPETITIONS; i++)
            {
                var key = generator(KEY_SIZE); 
                foreach (var ch in key) counts[ch]++;
            }

            int totalChars = counts.Values.Sum();
            foreach (var ch in UniqueKey.KeyGenerator.chars)
            {
                Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
            }
        }
    }
}

11
Це виглядає як правильний підхід до мене - випадкові паролі, солі, ентропія тощо не повинні створюватися за допомогою Random (), який оптимізований для швидкості та генерує відтворювані послідовності чисел; RNGCryptoServiceProvider.GetNonZeroBytes (), з іншого боку, створює дикі послідовності чисел, які НЕ відтворюються.
mindplay.dk

25
Букви трохи упереджені (255% 62! = 0). Незважаючи на цю незначну ваду, це далеко не найкраще рішення.
CodesInChaos

13
Зауважте, що це не здорово, якщо ви хочете криптовалюти, неупередженої випадковості. (І якщо ви цього не хочете, то навіщо використовувати RNGCSPв першу чергу?) Використання мода для індексації в charsмасив означає, що ви отримаєте упереджений вихід, якщо chars.Lengthне стане дільником 256.
LukeH

15
Одна з можливостей значно зменшити ухил - це запит 4*maxSizeвипадкових байтів, а потім використання (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. Я б також використовував GetBytesзамість GetNonZeroBytes. І нарешті ви можете видалити перший дзвінок до GetNonZeroBytes. Ви не використовуєте його результат.
CodesInChaos

14
Факт забави: AZ az 0-9 - 62 символи. Люди вказують на зміщення листів, оскільки 256% 62! = 0. Ідентифікатори відео YouTube - AZ az 0-9, а також "-" та "_", що створює 64 можливі символи, які ділиться на 256 рівномірно. Збіг? Я думаю, НЕ! :)
qJake

200

Рішення 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" - принаймні у версії 6 GUID.

Це рішення також дозволяє створювати рядок будь-якої довжини.

Рішення 2 - Один рядок коду - хороший до 22 символів

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

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

Рішення 3 - Трохи менше коду

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

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

Що означає більше шансів зіткнення - протестуючи його за допомогою 100 000 повторень з 8 символьних рядків, генерується один дублікат.


22
Ви насправді створили дублікат? Дивно, що 5,316,911,983,139,663,491,615,228,241,121,400,000 можливі комбінації GUID.
Алекс

73
@Alex: Він скорочує GUID до 8 символів, тому ймовірність зіткнень набагато вище, ніж у GUID.
dtb

23
Ніхто не може оцінити це, окрім дурнів :) Так, ви абсолютно праві, обмеження на 8 знаків має значення.
Алекс

31
Guid.NewGuid (). ToString ("n") не утримуватиме тире, не вимагає виклику Replace (). Але слід зазначити, GUID - це лише 0-9 та AF. Кількість комбінацій "досить добре", але ніде не доходить до того, що дозволяє справжня буквено-числова випадкова рядок. Шанси зіткнення 1: 4,294,967,296 - те саме, що і випадкове 32-бітове ціле число.
richardtallent

32
1) GUID призначені для унікальних, а не випадкових. Хоча нинішні версії Windows генерують V4 GUID, які справді є випадковими, це не гарантується. Наприклад, старіші версії Windows використовували GUI V1, де ваша могла б вийти з ладу. 2) Просто використання шістнадцяткових символів значно знижує якість випадкової рядки. Від 47 до 32 біт. 3) Люди недооцінюють ймовірність зіткнення, оскільки дають це окремим парам. Якщо ви генеруєте 100k 32-бітових значень, у вас, ймовірно, є одне зіткнення серед них. Дивіться проблему з днем ​​народження.
CodesInChaos

72

Ось приклад, який я вкрав у прикладу Сема Аллена у 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: дякую Сем


27
Це добре працює. Я провів це через 100 000 ітерацій і ніколи не мав повторюваного імені. Тим НЕ менше, я зробив знайти кілька вульгарних слів (англійською мовою). Навіть не подумав би про це, хіба що один із перших у списку мав у ньому F ***. Якщо ви використовуєте це для чогось, що користувач побачить
techturtle

3
@techturtle Дякую за попередження. Я припускаю, що існує ризик виникнення вульгарних слів з будь-яким випадковим генерацією рядків, яке використовує всі літери в алфавіті.
Адам Порад

приємно і просто, але не добре для довгої струни ... проголосуйте за цей добрий трюк
Maher Abuthraa

Цей метод, здається, повертає лише рядкові буквено-цифрові рядки.
jaybro

2
Час від часу виникають вульгарні слова, але якщо ви продовжуєте цю роботу досить довго, вона з часом пише Шекспіра. (Всього кілька
днів

38

Основні цілі мого коду:

  1. Розподіл рядків майже рівномірний (небайдужи про незначні відхилення, якщо вони невеликі)
  2. Він виводить більше декількох мільярдів рядків для кожного набору аргументів. Створення рядка з 8 символів (~ 47 біт ентропії) безглуздо, якщо ваш PRNG генерує лише 2 мільярди (31 біт ентропії) різних значень.
  3. Це безпечно, оскільки я очікую, що люди використовуватимуть це для паролів чи інших маркерів безпеки.

Перша властивість досягається, приймаючи значення 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];
    var result = new char[length];
    using (var cryptoProvider = new RNGCryptoServiceProvider())
    {
        cryptoProvider.GetBytes(bytes);
    }
    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) немає. Тож якщо більша кількість зменшує упередженість, це не усуває. Я оновив свою відповідь нижче, мій підхід полягав у тому, щоб відкинути випадкові введення та замінити іншим значенням.
Тодд

@Todd Я знаю, що це не усуває зміщення, але я вибрав простоту цього рішення над усуненням практично невідповідних упереджень.
CodesInChaos

Я згоден, у більшості випадків це, мабуть, практично не має значення. Але тепер я оновив свою шахту, щоб бути настільки ж швидкою, як Random, і трохи безпечнішою, ніж ваша. Усі відкриті джерела для всіх. Так, я витратив занадто багато часу на це ...
Тодд

Якщо ми використовуємо постачальника RNG, чи є у нас якийсь спосіб уникнути теоретичних упереджень? Я не впевнений ... Якщо Тодд означає спосіб, коли він генерує додаткове випадкове число (коли ми перебуваємо в зоні зміщення), то це може бути помилковим припущенням. RNG має майже лінійний розподіл всіх генерованих значень в середньому. Але це не означає, що у нас не буде локальної кореляції між генерованими байтами. Тож додатковий байт тільки для зони зміщення може все-таки дати нам деяку зміщення, але через іншу причину. Швидше за все, це зміщення буде дуже малим. Але в цьому випадку збільшення загальної кількості генерованих байтів є більш простим способом.
Максим

1
@Maxim Ви можете використовувати відмову, щоб повністю усунути зміщення (припустимо, що основний генератор є абсолютно випадковим). В обмін код може працювати довільно довго (з експоненціально малою ймовірністю).
CodesInChaos

32

Найпростіший:

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є великим некрасивим, оскільки воно покладається на розмір виводу, що становить не більше розміру алфавіту.
CodesInChaos

@CodesInChaos Я не впевнений, чи розумію тебе. Ви маєте на увазі 'a'.To('z')підхід?
nawfal

1
1) chars.Select().Take (n) `працює лише якщо chars.Count >= n. Вибір послідовності, яку ви насправді не використовуєте, трохи неінтуїтивний, особливо з цим неявним обмеженням довжини. Я вважаю за краще використовувати Enumerable.Rangeабо Enumerable.Repeat. 2) Повідомлення про помилку «кінцевий символ повинен бути менше початкової напівкоксу» неправильний шлях круглий / пропускаючи ні not.
CodesInChaos

@CodesInChaos, але в моєму випадку chars.Countце спосіб > n. Також я не отримую неінтуїтивної частини. Це робить все, що використовує Takeнеінтуїтивно, чи не так? Я не вірю в це. Дякуємо, що вказали на друкарську помилку.
nawfal

4
Це розміщено на theDailyWTF.com як стаття CodeSOD.

22

Лише деякі порівняння продуктивності різних відповідей у ​​цій темі:

Методи та налаштування

// what's available
public static string possibleChars = "abcdefghijklmnopqrstuvwxyz";
// optimized (?) what's 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]
  • від циклу = gtnoaryhxr [10]
  • від 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
Було б цікаво дізнатися, які з них створили дупи.
Ребекка

@Junto - щоб з'ясувати, що призводить до дублікатів, чогось подібного var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, де ви замінюєте EachRandomizingMethod... кожен метод
drzaus

19

Один рядок коду Membership.GeneratePassword()робить трюк :)

Ось демонстрація того ж.


1
Схоже, Microsoft перейшла за посиланням. Ще один зразок коду знаходиться за адресою msdn.microsoft.com/en-us/library/ms152017 або aspnet.4guysfromrolla.com/demos/GeneratePassword.aspx або developer.xamarin.com/api/member/…
Пуран

Я подумав про це, але не зміг позбутися не буквено-цифрових символів, оскільки другим аргументом є МІНІМАЛЬНІ алфавітні символи
ozzy432836

13

Код, написаний Еріком Дж., Досить неохайний (цілком зрозуміло, що це з 6 років тому ... він, мабуть, не писав би цей код сьогодні), і є навіть деякі проблеми.

На відміну від деяких представлених альтернатив, ця криптографічно звучить.

Неправда ... У паролі є ухил (як написано в коментарі), bcdefghтрохи більш вірогідний, ніж інші ( aне тому, що GetNonZeroBytesвін не генерує байти зі значенням нуля, тому зміщення для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);
    }
}

7

Ми також використовуємо нестандартні рядкові випадкові, але ми реалізовані як помічник рядка, тому це забезпечує певну гнучкість ...

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

7

Мій простий однорядковий код працює для мене :)

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

Мені також подобається метод
наведення

6

Іншим варіантом може бути використання 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());

6

Питання: Чому я повинен витрачати свій час на використання, 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;
    }
}

Відповідь: Чарівні струни - БАД. Хтось помітив, що не було "I в моєму рядку вгорі "" немає? Моя мати навчила мене не використовувати магічні струни саме з цієї причини ...

nb 1: Як сказали багато інших, як @dtb, не використовуйте, System.Randomякщо вам потрібна криптографічна безпека ...

nb 2: Ця відповідь не є найефективнішою чи найкоротшою, але я хотів виділити відповідь на питання. Мета моєї відповіді - скоріше застерегти від магічних рядків, ніж дати фантастичну інноваційну відповідь.


Чому мені байдуже, що немає " I?"
Крістін

1
Буквено-цифровий (ігнорування відмінка) є [A-Z0-9]. Якщо випадково ваш випадковий рядок лише коли-небудь покриває [A-HJ-Z0-9]результат, він не покриває повний допустимий діапазон, що може бути проблематично.
Вай Ха Лі

Як це може бути проблематично? Тому він не містить I. Це тому, що є один менший символ, і це полегшує злом? Які статистичні дані щодо паролів, що складаються, що містять 35 символів у діапазоні, ніж 36. Я думаю, що я скоріше ризикую ... або просто підтверджую діапазон символів ... ніж включаю все те додаткове сміття в мій код. Але, це я. Я маю на увазі, щоб не бути стиковою дірою, я просто говорю. Іноді я думаю, що програмісти мають тенденцію йти позаскладним маршрутом заради того, щоб бути надскладним.
Крістін

1
Це поглиблюється у випадку використання. Дуже часто виключати такі символи, як Iі Oз цих типів випадкових рядків, щоб люди не плутали їх з 1і 0. Якщо вам не байдуже, що у вас є людський читабельний рядок, добре, але якщо це щось, кому може знадобитися набрати, то насправді розумно видалити ці символи.
Кріс Пратт

5

Трохи чистіша версія рішення 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);

Ваші налаштування стилю можуть відрізнятися.


Це набагато краще і ефективніше, ніж прийнята відповідь.
Клин

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

5

Переглянувши інші відповіді та розглянувши коментарі 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, щоб полегшити копіювання та переконатися, що у вас є остання версія, я думаю, що це посилання переходить до конкретної версії коду, а не останньої).

Оновлені нотатки:

  1. Що стосується деяких інших відповідей - Якщо ви знаєте довжину виводу, вам не потрібен StringBuilder, а при використанні ToCharArray це створює і заповнює масив (спочатку вам не потрібно створювати порожній масив)
  2. Що стосується деяких інших відповідей - Вам слід скористатися NextBytes, а не отримувати їх по черзі для виконання
  3. Технічно ви могли закріпити байтовий масив для швидшого доступу. Це, як правило, того варто, коли ваш ітератор перевищує 6-8 разів через байтовий масив. (Тут не робиться)
  4. Використання RNGCryptoServiceProvider для найкращої випадковості
  5. Використання кешування 1 МБ буфера випадкових даних - бенчмаркінг показує кешовану однобайтову швидкість доступу ~ 1000x швидше - займає 9 мс за 1 МБ проти 989 мс для кешування.
  6. Оптимізоване відхилення зони зміщення в моєму новому класі.

Кінцеве рішення питання:

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

Для історії - моє старе рішення для цієї відповіді, використано об'єкт Random:

    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's still a bias on index 0.
                  rBytes[i] = rGen.NextByte();
              rName[i] = charSet[rBytes[i] % charSet.Length];
          }
      }
      return new string(rName);
    }

Продуктивність:

  1. SecureFastRandom - Перший одиночний запуск = ~ 9-33ms . Непомітний. Триває : 5 мс (іноді доходить до 13 мс ) за 10 000 ітерацій, з єдиною середньою ітерацією = 1,5 мікросекунди. . Примітка. Потрібно, як правило, 2, але іноді до 8 кеш-оновлень - залежить від того, скільки одиничних байтів перевищує зону зміщення
  2. Випадковий - перший разовий пробіг = ~ 0-1мс . Непомітний. Триває : 5 мс понад 10 000 ітерацій. З однією середньою ітерацією = .5 мікросекунд. . Приблизно однакова швидкість.

Також перевірте:

Ці посилання - ще один підхід. Буферизація може бути додана до цієї нової бази кодів, але найважливішим було вивчення різних підходів до усунення упередженості та порівняльний аналіз швидкості та плюсів / мінусів.


Я знайшов деякі незначні поліпшення роботи вашого методу, який , здавалося , голодному згустку - stackoverflow.com/a/17092645/1037948
drzaus

5
1) Чому всі ці магічні константи? Ви вказали довжину вихідний три рази. Просто визначте це як константу або параметр. Ви можете використовувати charSet.Lengthзамість 62. 2) Статичний Randomбез блокування означає, що цей код не є безпечним для потоків. 3) зменшення 0-255 мод 62 вводить помітний зміщення. 4) Ви не можете використовувати ToStringмасив char, який завжди повертається "System.Char[]". Вам потрібно використовувати new String(rName)замість цього.
CodesInChaos

Дякую @CodesInChaos, я ніколи не думав про ці речі тоді. Ще тільки використовую клас Random, але це має бути краще. Я не міг придумати кращого способу виявлення та виправлення вхідних даних.
Тодд

Починати зі слабкого RNG ( System.Random), а потім обережно уникати будь-яких упереджень у власному коді, трохи нерозумно . На думку приходить вираз «полірувати качан».
CodesInChaos

@CodesInChaos А тепер учень перевершив свого майстра
Тодд

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


4

Я шукав більш конкретну відповідь, де я хочу контролювати формат випадкової рядки і натрапив на цю посаду. Наприклад: номерні знаки (автомобілів) мають певний формат (для кожної країни), і я хотів створити випадкові номерні знаки.
Для цього я вирішив написати власний метод розширення Random. . Я створив суть ( 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

Тепер в ароматі одного лайнера.

private string RandomName()
{
        return new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    using (var cryptoProvider = new RNGCryptoServiceProvider())
                        cryptoProvider.GetBytes(cryptoResult);

                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());
}

2
Використання властивості для чогось, що змінюється в кожному доступі, є досить сумнівним. Я б рекомендував замість цього використовувати метод.
CodesInChaos

2
RNGCryptoServiceProviderслід утилізувати після використання.
Цахі Ашер

Я виправив проблему IDisposable, але це все ще дуже сумнівно, створюючи новий RNGCryptoServiceProvider для кожного листа.
piojo

@CodesInChaos зроблено, тепер метод.
Матас Вайткевічус

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 algorithm 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())
    }

1) Ви можете використовувати літеральні символи замість значень ASCII, пов'язаних з цими символами. 2) У вашому коді відповідності інтервалу помилка по одному. Вам потрібно використовувати <=і >=замість <і >. 3) Я б додав непотрібні дужки навколо &&виразів, щоб зрозуміти, що вони мають перевагу, але, звичайно, це лише стилістичний вибір.
CodesInChaos

+ 1 Добре для усунення зміщення та додавання тестування. Я не впевнений, чому ви додаєте довільну рядок разом із рядком, похідним часовою міткою? Крім того, вам все одно потрібно розпоряджатися своїм RNGCryptoServiceProvider
monty

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

Ось варіант рішення Еріка Дж, тобто криптографічно звуковий, для 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 символи.
Лежи Райан

1

Я знаю, що це не найкращий спосіб. Але ви можете спробувати це.

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);

2
Як це один рядок? Console.WriteLine ($ "Випадкова рядок: {Path.GetRandomFileName (). Замінити (". "," ")}"); - це один рядок.
PmanAce

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

Зрозуміло, вона не оптимізована для швидкості, тому, якщо важливо місія для створення мільйонів випадкових рядків щосекунди, спробуйте ще одну!

ПРИМІТКА. Це рішення не дозволяє повторювати символи в алфавіті, а алфавіт ОБОВ'ЯЗКОВО повинен бути рівним або більшим розміром, ніж вихідний рядок, що робить такий підхід в деяких умовах менш бажаним, все залежить від вашого використання.


0

Якщо ваші значення не є абсолютно випадковими, але насправді можуть залежати від чогось - ви можете обчислити хdd md5 або sha1 цього 'somwthing', а потім обрізати його будь-якої довжини.

Також ви можете генерувати та скорочувати орієнтири.


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прямого? Ускладнює код і нічого корисного не досягає.
CodesInChaos

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

Ви заявили GenerateRandomStringі подзвонили GetRandomStringзсередини SanitiseBase64String. Крім того, ви заявили , SanitiseBase64String і виклик CleanUpBase64Stringв GenerateRandomString.
Вай Ха Лі


0

не на 100% впевнений, оскільки я тут не перевіряв КОЖЕН варіант, але з тих, що я робив тест, цей є найшвидшим. приурочила його до секундоміра, і він показав 9-10 тиків, тому якщо швидкість важливіша за безпеку, спробуйте це:

 private static Random random = new Random(); 
 public static string Random(int length)
     {   
          var stringChars = new char[length];

          for (int i = 0; i < length; i++)
              {
                  stringChars[i] = (char)random.Next(0x30, 0x7a);                  
                  return new string(stringChars);
              }
     }

Навіщо відповідати? На це запитання давали відповіді багато разів, і все ж ви все ще залишаєте свою напівробочу відповідь ..
Лоран
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.