Як я можу генерувати випадкову буквено-цифрову рядок у 8 символів у C #?
Randomкласу для генерації паролів. Насіннєвий матеріал Randomмає дуже низьку ентропію, тому це не дуже безпечно. Використовуйте криптографічний PRNG для паролів.
Як я можу генерувати випадкову буквено-цифрову рядок у 8 символів у C #?
Randomкласу для генерації паролів. Насіннєвий матеріал Randomмає дуже низьку ентропію, тому це не дуже безпечно. Використовуйте криптографічний PRNG для паролів.
Відповіді:
Я чув, що 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клас, якщо вам потрібен сильний генератор випадкових чисел.)
return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
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клас, якщо вам потрібен сильний генератор випадкових чисел.)
GetRandomFileNameшвидше, але не дозволяє контролювати використовувані символи, а максимальна можлива довжина - 11 символів. GuidРішення Дугласа блискавично, але символи обмежені A-F0-9, а максимальна можлива довжина - 32 символи.
GetRandomFileNameале тоді (a) ви втратите свою перевагу в роботі та (b) ваш код ускладниться.
System.Randomне підходить для безпеки.
ОНОВЛЕНО на основі коментарів. Початкова реалізація генерувала 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")}%");
}
}
}
}
RNGCSPв першу чергу?) Використання мода для індексації в charsмасив означає, що ви отримаєте упереджений вихід, якщо chars.Lengthне стане дільником 256.
4*maxSizeвипадкових байтів, а потім використання (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. Я б також використовував GetBytesзамість GetNonZeroBytes. І нарешті ви можете видалити перший дзвінок до GetNonZeroBytes. Ви не використовуєте його результат.
Рішення 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 символьних рядків, генерується один дублікат.
Ось приклад, який я вкрав у прикладу Сема Аллена у 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: дякую Сем
Основні цілі мого коду:
Перша властивість досягається, приймаючи значення 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);
}
Найпростіший:
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наприклад.
chars.Selectє великим некрасивим, оскільки воно покладається на розмір виводу, що становить не більше розміру алфавіту.
'a'.To('z')підхід?
chars.Select().Take (n) `працює лише якщо chars.Count >= n. Вибір послідовності, яку ви насправді не використовуєте, трохи неінтуїтивний, особливо з цим неявним обмеженням довжини. Я вважаю за краще використовувати Enumerable.Rangeабо Enumerable.Repeat. 2) Повідомлення про помилку «кінцевий символ повинен бути менше початкової напівкоксу» неправильний шлях круглий / пропускаючи ні not.
chars.Countце спосіб > n. Також я не отримую неінтуїтивної частини. Це робить все, що використовує Takeнеінтуїтивно, чи не так? Я не вірю в це. Дякуємо, що вказали на друкарську помилку.
Лише деякі порівняння продуктивності різних відповідей у цій темі:
// 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 мс) найнижчо [Різна машина]
var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, де ви замінюєте EachRandomizingMethod... кожен метод
Один рядок коду Membership.GeneratePassword()робить трюк :)
Ось демонстрація того ж.
Код, написаний Еріком Дж., Досить неохайний (цілком зрозуміло, що це з 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);
}
}
Ми також використовуємо нестандартні рядкові випадкові, але ми реалізовані як помічник рядка, тому це забезпечує певну гнучкість ...
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);
Мій простий однорядковий код працює для мене :)
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);
}
Іншим варіантом може бути використання 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());
Питання: Чому я повинен витрачати свій час на використання, 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?"
[A-Z0-9]. Якщо випадково ваш випадковий рядок лише коли-небудь покриває [A-HJ-Z0-9]результат, він не покриває повний допустимий діапазон, що може бути проблематично.
I. Це тому, що є один менший символ, і це полегшує злом? Які статистичні дані щодо паролів, що складаються, що містять 35 символів у діапазоні, ніж 36. Я думаю, що я скоріше ризикую ... або просто підтверджую діапазон символів ... ніж включаю все те додаткове сміття в мій код. Але, це я. Я маю на увазі, щоб не бути стиковою дірою, я просто говорю. Іноді я думаю, що програмісти мають тенденцію йти позаскладним маршрутом заради того, щоб бути надскладним.
Iі Oз цих типів випадкових рядків, щоб люди не плутали їх з 1і 0. Якщо вам не байдуже, що у вас є людський читабельний рядок, добре, але якщо це щось, кому може знадобитися набрати, то насправді розумно видалити ці символи.
Трохи чистіша версія рішення 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);
Ваші налаштування стилю можуть відрізнятися.
Переглянувши інші відповіді та розглянувши коментарі 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, щоб полегшити копіювання та переконатися, що у вас є остання версія, я думаю, що це посилання переходить до конкретної версії коду, а не останньої).
Оновлені нотатки:
Кінцеве рішення питання:
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);
}
Продуктивність:
Також перевірте:
Ці посилання - ще один підхід. Буферизація може бути додана до цієї нової бази кодів, але найважливішим було вивчення різних підходів до усунення упередженості та порівняльний аналіз швидкості та плюсів / мінусів.
charSet.Lengthзамість 62. 2) Статичний Randomбез блокування означає, що цей код не є безпечним для потоків. 3) зменшення 0-255 мод 62 вводить помітний зміщення. 4) Ви не можете використовувати ToStringмасив char, який завжди повертається "System.Char[]". Вам потрібно використовувати new String(rName)замість цього.
System.Random), а потім обережно уникати будь-яких упереджень у власному коді, трохи нерозумно . На думку приходить вираз «полірувати качан».
Я жахливо знаю, але я просто не міг собі допомогти:
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);
}
}
}
Я шукав більш конкретну відповідь, де я хочу контролювати формат випадкової рядки і натрапив на цю посаду. Наприклад: номерні знаки (автомобілів) мають певний формат (для кожної країни), і я хотів створити випадкові номерні знаки.
Для цього я вирішив написати власний метод розширення 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)];
}
}
Тепер в ароматі одного лайнера.
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());
}
RNGCryptoServiceProviderслід утилізувати після використання.
Спробуйте поєднати дві частини: унікальну (послідовність, лічильник або дата) та випадкову
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())
}
<=і >=замість <і >. 3) Я б додав непотрібні дужки навколо &&виразів, щоб зрозуміти, що вони мають перевагу, але, звичайно, це лише стилістичний вибір.
Рішення без використання Random:
var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);
var randomStr = new string(chars.SelectMany(str => str)
.OrderBy(c => Guid.NewGuid())
.Take(8).ToArray());
Ось варіант рішення Еріка Дж, тобто криптографічно звуковий, для 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();
}
Я знаю, що це не найкращий спосіб. Але ви можете спробувати це.
string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
Я не знаю, наскільки це криптографічно звучить, але це читабельніше і стисліше, ніж більш хитромудрі рішення на сьогоднішній день (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());
Зрозуміло, вона не оптимізована для швидкості, тому, якщо важливо місія для створення мільйонів випадкових рядків щосекунди, спробуйте ще одну!
ПРИМІТКА. Це рішення не дозволяє повторювати символи в алфавіті, а алфавіт ОБОВ'ЯЗКОВО повинен бути рівним або більшим розміром, ніж вихідний рядок, що робить такий підхід в деяких умовах менш бажаним, все залежить від вашого використання.
Якщо ваші значення не є абсолютно випадковими, але насправді можуть залежати від чогось - ви можете обчислити хdd md5 або sha1 цього 'somwthing', а потім обрізати його будь-якої довжини.
Також ви можете генерувати та скорочувати орієнтири.
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;
}
}
random.Nextпрямого? Ускладнює код і нічого корисного не досягає.
Ось механізм для генерування випадкової буквено-числової рядки (я використовую це для створення паролів і тестових даних) без визначення алфавіту та чисел,
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.
Існує один із дивовижних пакунків, що робить це таким простим.
var myObject = new Faker<MyObject>()
.RuleFor(p => p.MyAlphaNumericProperty, f => f.Random.AlphaNumeric(/*lenght*/ 7))
.Generate();
Один з хороших прикладів тут .
не на 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);
}
}