Унікальна генерація випадкових рядків


97

Я б хотів генерувати випадкові унікальні рядки, наприклад ті, що створюються бібліотекою MSDN (наприклад, об’єкт помилки ). Слід створити рядок типу "t9zk6eay".


1
спробуйте це string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);більше можна знайти тут
shaijut

1
Щоб щось було абсолютно унікальним, воно повинно базуватися на чомусь невипадковому, як-от час, місце розташування тощо, і тому насправді ніколи не може бути повністю випадковим. Посібник може здатися випадковим, але насправді це не так. Єдина ваша надія - зробити це настільки випадковим і складним, що для всіх практичних цілей значення будуть унікальними (тобто мають надзвичайно низьку ймовірність зіткнення).
bytedev

Відповіді:


84

Використання Guid було б досить хорошим способом, але щоб отримати щось схоже на ваш приклад, ви, ймовірно, хочете перетворити його на рядок Base64:

    Guid g = Guid.NewGuid();
    string GuidString = Convert.ToBase64String(g.ToByteArray());
    GuidString = GuidString.Replace("=","");
    GuidString = GuidString.Replace("+","");

Я позбавляюся від "=" та "+", щоб трохи наблизитись до вашого прикладу, інакше ви отримуєте "==" у кінці вашого рядка та "+" в середині. Ось приклад вихідного рядка:

"OZVV5TpP4U6wJthaCORZEQ"


15
Вам слід подумати про заміну / теж.
Джейсон Кілі, 04

20
Посібник не слід розглядати як безпечну випадкову рядок, оскільки про послідовність можна здогадатися. Посібник призначений для уникнення ключових конфліктів, а не випадкових. Існує кілька хороших дискусій про випадковість Guid навколо переповнення стека.
Даніель Бредлі

Для чіткого та короткого пояснення того, що Convert.ToBase64Stringйдеться, подивіться тут .
jwaliszko

2
Чи може перетворення орієнтирів у base64 та заміна + і = збільшення ймовірності зіткнення?
Мілан Агарвал

5
@SimonEjsing Я запрошу вас на пиво, якщо ви насправді можете написати програму, яка отримує зіткнення при використанні new Guid()без "злому" (підробка годинника або внутрішня структура даних Windows). Сміливо використовуйте стільки ядер, ниток, примітивів для синхронізації тощо.
Lucero

175

Оновлення 2016/1/23

Якщо ви вважаєте, що ця відповідь є корисною, вас може зацікавити проста бібліотека створення паролів (~ 500 SLOC), яку я опублікував :

Install-Package MlkPwgen

Тоді ви можете генерувати випадкові рядки так само, як у відповіді нижче:

var str = PasswordGenerator.Generate(length: 10, allowed: Sets.Alphanumerics);

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

Оригінальний відповідь

Оскільки ще ніхто не надав захищений код, я публікую наступне, якщо хтось вважає це корисним.

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

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

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

Дякуємо Ахмеду за те, що він вказав, як отримати код, що працює над .NET Core.


Рішення @Keltex не працювало належним чином для мене (воно повертало той самий рядок після кількох використання). Це рішення працює ідеально :)
JoanComasFdz

2
@LeeGrissom, упередження є важливим аспектом. Скажімо, наприклад, що ваш алфавіт містить 255 символів, і ви отримуєте випадкове значення від 0 до 255. У буфері кільця як значення 0, так і 255 відповідали б одному і тому ж символу, який зміщував би результат на користь першого символу в алфавіті, це було б менш випадковим. якщо це має значення залежить від застосування курсу.
Оскар Шеберг

4
Хто націлений .netcore: Замінити var rng = new RNGCryptoServiceProvider()наvar rng = RandomNumberGenerator.Create()
amd

1
Чому ви обчислюєте 'var outOfRangeStart = byteSize - (byteSize% allowedCharSet.Length);' за кожну ітерацію? Ви можете обчислити його перед "використанням".
mtkachenko

1
@BartCalixto Виправлено. Дякую!
Михайло Кропат

38

Я застережу, що GUID не є випадковими числами . Вони не повинні використовуватися як основа для створення всього, що, як ви очікуєте, буде абсолютно випадковим (див. Http://en.wikipedia.org/wiki/Globally_Unique_Identifier ):

Криптоаналіз генератора WinAPI GUID показує, що, оскільки послідовність V4 GUID є псевдовипадковими, з огляду на початковий стан можна передбачити до наступних 250 000 GUID, повернених функцією UuidCreate. Ось чому GUID не слід використовувати в криптографії, наприклад, як випадкові ключі.

Замість цього просто використовуйте метод C # Random. Щось подібне ( код знайдено тут ):

private string RandomString(int size)
{
  StringBuilder builder = new StringBuilder();
  Random random = new Random();
  char ch ;
  for(int i=0; i<size; i++)
  {
    ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))) ;
    builder.Append(ch);
  }
  return builder.ToString();
}

GUID - це добре, якщо ви хочете щось унікальне (наприклад, унікальне ім’я файлу або ключ у базі даних), але вони не дуже зручні для того, що ви хочете бути випадковим (наприклад, пароль або ключ шифрування). Тож це залежить від вашої заявки.

Редагувати . Microsoft каже, що Random також не такий великий ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ):

Для створення криптографічно захищеного випадкового числа, придатного для створення випадкового пароля, наприклад, використовуйте клас, похідний від System.Security.Cryptography.RandomNumberGenerator, такий як System.Security.Cryptography.RNGCryptoServiceProvider.


5
Клас C # random не є також "випадковим" і не підходить для будь-якого криптокоду, оскільки це класичний генератор випадкових випадків, починаючи з певного насіннєвого числа. Те саме насіння також поверне ту саму послідовність повернутих чисел; підхід GUID тут вже набагато кращий (не "випадковий", а "унікальний").
Lucero

3
@Lucero: Ви праві. Microsoft рекомендує: "Для створення криптографічно захищеного випадкового числа, придатного для створення випадкового пароля, наприклад, використовуйте клас, похідний від System.Security.Cryptography.RandomNumberGenerator, такий як System.Security.Cryptography.RNGCryptoServiceProvider."
Keltex

Ну, у запитанні вже було сказано, що він хоче (псевдо-) випадкові унікальні рядки, тому немає вимог до криптографії або навіть необхідності слідувати певному випадковому розподілу. Тож GUID - це, мабуть, найпростіший підхід.
Джої

1
Заява про те, що "з огляду на початковий стан можна передбачити до наступних 250 000 GUID", схоже на властиву правдивій заяві для будь-якого PRNG ... Я впевнений, що це також не є безпечним, але я не впевнений, що це велика цінність у створенні справді випадкові URL-адреси, якщо це те, для чого йде OP. ;)
ojrac

1
(+1 у будь-якому разі - важлива освіта
PRNG

13

Я спростив рішення @Michael Kropats і зробив версію LINQ-esque.

string RandomString(int length, string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
{       
    var outOfRange = byte.MaxValue + 1 - (byte.MaxValue + 1) % alphabet.Length;

    return string.Concat(
        Enumerable
            .Repeat(0, int.MaxValue)
            .Select(e => RandomByte())
            .Where(randomByte => randomByte < outOfRange)
            .Take(length)
            .Select(randomByte => alphabet[randomByte % alphabet.Length])
    );
}

byte RandomByte()
{
    using (var randomizationProvider = new RNGCryptoServiceProvider())
    {
        var randomBytes = new byte[1];
        randomizationProvider.GetBytes(randomBytes);
        return randomBytes.Single();
    }   
}

11

Я не думаю, що вони насправді випадкові, але я здогадуюсь, що це деякі хеші.

Кожен раз, коли мені потрібен якийсь випадковий ідентифікатор, я зазвичай використовую GUID і перетворюю його на його "голе" представлення:

Guid.NewGuid().ToString("n");

Як зазначав @Keltex: Криптоаналіз генератора WinAPI GUID показує, що, оскільки послідовність GUI V4 є псевдовипадковими, з огляду на початковий стан можна передбачити до наступних 250 000 GUID, повернених функцією UuidCreate.
JoanComasFdz

4

Спробуйте комбінувати між Guid та Time.Ticks

 var randomNumber = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) + DateTime.Now.Ticks;
     randomNumber = System.Text.RegularExpressions.Regex.Replace(randomNumber, "[^0-9a-zA-Z]+", "");

3

Я здивований, чому не існує рішення CrytpoGraphic. GUID є унікальним, але не криптографічно безпечним . Дивіться цю скрипку Dotnet.

var bytes = new byte[40]; // byte size
using (var crypto = new RNGCryptoServiceProvider())
  crypto.GetBytes(bytes);

var base64 = Convert.ToBase64String(bytes);
Console.WriteLine(base64);

Якщо ви хочете доповнити інструкцію:

var result = Guid.NewGuid().ToString("N") + base64;
Console.WriteLine(result);

Чистіший буквено-цифровий рядок:

result = Regex.Replace(result,"[^A-Za-z0-9]","");
Console.WriteLine(result);

1

Рішення Майкла Кропаца у VB.net

Private Function RandomString(ByVal length As Integer, Optional ByVal allowedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") As String
    If length < 0 Then Throw New ArgumentOutOfRangeException("length", "length cannot be less than zero.")
    If String.IsNullOrEmpty(allowedChars) Then Throw New ArgumentException("allowedChars may not be empty.")


    Dim byteSize As Integer = 256
    Dim hash As HashSet(Of Char) = New HashSet(Of Char)(allowedChars)
    'Dim hash As HashSet(Of String) = New HashSet(Of String)(allowedChars)
    Dim allowedCharSet() = hash.ToArray

    If byteSize < allowedCharSet.Length Then Throw New ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize))


    ' Guid.NewGuid and System.Random are not particularly random. By using a
    ' cryptographically-secure random number generator, the caller is always
    ' protected, regardless of use.
    Dim rng = New System.Security.Cryptography.RNGCryptoServiceProvider()
    Dim result = New System.Text.StringBuilder()
    Dim buf = New Byte(128) {}
    While result.Length < length
        rng.GetBytes(buf)
        Dim i
        For i = 0 To buf.Length - 1 Step +1
            If result.Length >= length Then Exit For
            ' Divide the byte into allowedCharSet-sized groups. If the
            ' random value falls into the last group and the last group is
            ' too small to choose from the entire allowedCharSet, ignore
            ' the value in order to avoid biasing the result.
            Dim outOfRangeStart = byteSize - (byteSize Mod allowedCharSet.Length)
            If outOfRangeStart <= buf(i) Then
                Continue For
            End If
            result.Append(allowedCharSet(buf(i) Mod allowedCharSet.Length))
        Next
    End While
    Return result.ToString()
End Function

1

Це прекрасно працює для мене

    private string GeneratePasswordResetToken()
    {
        string token = Guid.NewGuid().ToString();
        var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(token);
        return Convert.ToBase64String(plainTextBytes);
    }

0

Про це запитували різні мови. Ось одне питання щодо паролів, які також повинні бути застосовані і тут.

Якщо ви хочете використовувати рядки для скорочення URL-адрес, вам також знадобиться словник <> або перевірка бази даних, щоб побачити, чи вже використаний згенерований ідентифікатор.


0

Якщо ви хочете буквено-цифрові рядки з малі та великі літери ([a-zA-Z0-9]), ви можете використовувати Convert.ToBase64String () для швидкого та простого рішення.

Що стосується унікальності, перегляньте проблему з днем народження, щоб обчислити, наскільки ймовірне зіткнення (A) довжина створених рядків та (B) кількість створених рядків.

Random random = new Random();

int outputLength = 10;
int byteLength = (int)Math.Ceiling(3f / 4f * outputLength); // Base64 uses 4 characters for every 3 bytes of data; so in random bytes we need only 3/4 of the desired length
byte[] randomBytes = new byte[byteLength];
string output;
do
{
    random.NextBytes(randomBytes); // Fill bytes with random data
    output = Convert.ToBase64String(randomBytes); // Convert to base64
    output = output.Substring(0, outputLength); // Truncate any superfluous characters and/or padding
} while (output.Contains('/') || output.Contains('+')); // Repeat if we contain non-alphanumeric characters (~25% chance if length=10; ~50% chance if length=20; ~35% chance if length=32)

-1
  • не впевнений, що посилання Microsoft генеруються випадковим чином
  • подивіться на новий Guid (). ToString ()

4
Ви маєте на увазі Guid.NewGuid (). ToString () - Guid не має публічного конструктора
cjk

3
Ви, мабуть, праві, друкували без підтвердження. Я впевнений, оригінальний плакат має суть.
Фабіан Вілерс

-1

Отримайте унікальний ключ, використовуючи хеш-код GUID

public static string GetUniqueKey(int length)
{
    string guidResult = string.Empty;

    while (guidResult.Length < length)
    {
        // Get the GUID.
        guidResult += Guid.NewGuid().ToString().GetHashCode().ToString("x");
    }

    // Make sure length is valid.
    if (length <= 0 || length > guidResult.Length)
        throw new ArgumentException("Length must be between 1 and " + guidResult.Length);

    // Return the first length bytes.
    return guidResult.Substring(0, length);
}

Це прекрасно працює, але випадкові слова не містять унікальних символів. Персонажі повторюються, як-от 114e3 (два 1-х), eaaea (три a-e та два e), 60207 (два 0-х) і так далі. Як генерувати рядок Random без повторення символів з буквено-цифровою комбінацією?
vijay

@vijay: Оскільки ви отримуєте шістнадцяткові цифри, ви обмежуєтесь 16 символами та 16! можливі виходи. Випадкові рядки - це саме те, випадкове. Теоретично можна отримати рядок усіх (aaaaaaaaaaaaaaaaa). Це дуже неймовірно, але не більше, ніж будь-який інший випадковий рядок. Я не впевнений, для чого вам знадобиться це обмеження, але, додаючи символи до рядка, вставте їх у HashSet <T>, перевірте їх існування та додайте їх до рядка або пропустіть їх відповідно.
Кріс Доггетт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.