Уникальное генерирование случайных строк

Я хотел бы генерировать случайные уникальные строки, подобные тем, которые создаются библиотекой MSDN (например,объект ошибки). Строка типа 't9zk6eay' должна быть сгенерирована.

Ответ 1

Использование Guid было бы неплохим способом, но чтобы получить что-то похожее на ваш пример, вы, вероятно, захотите преобразовать его в строку Base64:

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

Я избавлюсь от "=" и "+", чтобы немного приблизиться к вашему примеру, иначе вы получите "==" в конце строки и "+" в середине. Вот пример выходной строки:

"OZVV5TpP4U6wJthaCORZEQ"

Ответ 2

Обновление 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.

Ответ 3

Я бы предупредил, что идентификаторы GUID не случайные числа. Они не должны использоваться в качестве основы для генерации всего, что вы ожидаете от совершенно случайного (см. http://en.wikipedia.org/wiki/Globally_Unique_Identifier):

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

Вместо этого просто используйте метод С# 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 отлично, если вы хотите что-то уникальное (например, уникальное имя файла или ключ в базе данных), но они не подходят для того, что вы хотите быть случайным ( как пароль или ключ шифрования). Это зависит от вашего приложения.

Edit. Microsoft говорит, что Random тоже не так хорош (http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx):

Чтобы создать криптографически безопасное случайное число, подходящее для создания случайного пароля, например, используйте класс, полученный из System.Security.Cryptography.RandomNumberGenerator, например System.Security.Cryptography.RNGCryptoServiceProvider.

Ответ 4

Я упростил решение @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();
    }   
}

Ответ 5

Я не думаю, что они действительно случайны, но я предполагаю, что это некоторые хеши.

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

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

Ответ 6

Попробуйте комбинацию между Guid и Time.Ticks

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

Ответ 7

Решение Michael Kropats в 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

Ответ 8

Я удивлен, почему нет решения CrytpoGraphic на месте. GUID уникален, но не криптографически безопасен. Смотрите эту Dotnet Fiddle.

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

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

В случае, если вы хотите подготовить с Guid:

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

Очиститель буквенно-цифровой строки:

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

Ответ 9

Это задано для разных языков. Здесь один вопрос о паролях, который должен быть применим и здесь.

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

Ответ 10

Если вам нужны буквенно-цифровые строки с строчными и прописными буквами ([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)

Ответ 11

Это прекрасно работает для меня

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

Ответ 12

  • не уверен, что ссылка Microsoft генерируется случайным образом
  • посмотрите на новый Guid(). ToString()

Ответ 13

Получить уникальный ключ с использованием кода хэш-кода 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);
}