Bagaimana saya bisa menghasilkan string alfanumerik 8 karakter acak dalam C #?
Random
kelas untuk menghasilkan kata sandi. Penyemaian Random
entropi sangat rendah, sehingga tidak benar-benar aman. Gunakan PRNG kriptografi untuk kata sandi.
Bagaimana saya bisa menghasilkan string alfanumerik 8 karakter acak dalam C #?
Random
kelas untuk menghasilkan kata sandi. Penyemaian Random
entropi sangat rendah, sehingga tidak benar-benar aman. Gunakan PRNG kriptografi untuk kata sandi.
Jawaban:
Saya mendengar LINQ adalah hitam baru, jadi inilah upaya saya menggunakan 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());
}
(Catatan: Penggunaan Random
kelas membuat ini tidak cocok untuk keamanan apa pun yang terkait , seperti membuat kata sandi atau token. Gunakan RNGCryptoServiceProvider
kelas jika Anda memerlukan generator nomor acak yang kuat.)
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);
Tidak seanggun solusi Linq.
(Catatan: Penggunaan Random
kelas membuat ini tidak cocok untuk keamanan apa pun yang terkait , seperti membuat kata sandi atau token. Gunakan RNGCryptoServiceProvider
kelas jika Anda memerlukan generator nomor acak yang kuat.)
GetRandomFileName
lebih cepat tetapi tidak memungkinkan kontrol karakter yang digunakan dan panjang maksimal yang mungkin adalah 11 karakter. Guid
Solusi Douglas adalah kilat-cepat tetapi karakter dibatasi untuk A-F0-9 dan panjang maksimal yang mungkin adalah 32 karakter.
GetRandomFileName
tetapi kemudian (a) Anda akan kehilangan keunggulan kinerja Anda, dan (b) kode Anda akan menjadi lebih rumit.
System.Random
tidak cocok untuk keamanan.
DIPERBARUI berdasarkan komentar. Implementasi asli menghasilkan ah ~ 1,95% dari waktu dan karakter yang tersisa ~ 1,56% dari waktu. Pembaruan menghasilkan semua karakter ~ 1,61% dari waktu.
FRAMEWORK DUKUNGAN -. NET Core 3 (dan platform masa depan yang mendukung .NET Standard 2.1 atau lebih tinggi) memberikan metode suara cryptographically RandomNumberGenerator.GetInt32 () untuk menghasilkan bilangan bulat acak dalam rentang yang diinginkan.
Tidak seperti beberapa alternatif yang disajikan, yang satu ini secara kriptografi masuk akal .
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();
}
}
}
Berdasarkan pembahasan alternatif di sini dan diperbarui / dimodifikasi berdasarkan komentar di bawah ini.
Berikut ini adalah test harness kecil yang menunjukkan distribusi karakter dalam output yang lama dan yang diperbarui. Untuk diskusi mendalam tentang analisis keacakan , lihat 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
pertama kali?) Menggunakan mod untuk mengindeks ke dalam chars
array berarti bahwa Anda akan mendapatkan output yang bias kecuali chars.Length
kebetulan menjadi pembagi 256.
4*maxSize
byte acak, lalu gunakan (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length
. Saya juga akan menggunakan GetBytes
bukan GetNonZeroBytes
. Dan akhirnya Anda dapat menghapus panggilan pertama ke GetNonZeroBytes
. Anda tidak menggunakan hasilnya.
Solusi 1 - 'rentang' terbesar dengan panjang paling fleksibel
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);
}
}
Solusi ini memiliki rentang yang lebih banyak daripada menggunakan GUID karena GUID memiliki beberapa bit tetap yang selalu sama dan karenanya tidak acak, misalnya 13 karakter dalam hex selalu "4" - setidaknya dalam GUID versi 6.
Solusi ini juga memungkinkan Anda menghasilkan string dengan panjang berapa pun.
Solusi 2 - Satu baris kode - baik untuk hingga 22 karakter
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);
Anda tidak dapat membuat string selama Solusi 1 dan string tidak memiliki rentang yang sama karena bit yang diperbaiki di GUID, tetapi dalam banyak kasus ini akan melakukan pekerjaan.
Solusi 3 - Kode sedikit lebih sedikit
Guid.NewGuid().ToString("n").Substring(0, 8);
Sebagian besar menyimpan ini di sini untuk tujuan historis. Ini menggunakan kode sedikit lebih sedikit, yang meskipun datang sebagai biaya memiliki rentang kurang - karena menggunakan hex bukan base64 dibutuhkan lebih banyak karakter untuk mewakili kisaran yang sama dibandingkan solusi lain.
Yang berarti lebih banyak peluang tabrakan - mengujinya dengan 100.000 iterasi dari 8 string karakter yang dihasilkan satu duplikat.
Inilah contoh yang saya curi dari contoh Sam Allen di Dot Net Perls
Jika Anda hanya membutuhkan 8 karakter, gunakan Path.GetRandomFileName () di namespace System.IO. Sam mengatakan menggunakan "Path.GetRandomFileName di sini kadang-kadang lebih unggul, karena menggunakan RNGCryptoServiceProvider untuk keacakan yang lebih baik. Namun, terbatas pada 11 karakter acak."
GetRandomFileName selalu mengembalikan string 12 karakter dengan titik pada karakter ke-9. Jadi, Anda harus menghapus periode (karena itu tidak acak) dan kemudian mengambil 8 karakter dari string. Sebenarnya, Anda bisa mengambil 8 karakter pertama dan tidak khawatir tentang periode.
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
PS: terima kasih Sam
Tujuan utama kode saya adalah:
Properti pertama dicapai dengan mengambil modulo nilai alfabet 64 bit. Untuk huruf kecil (seperti 62 karakter dari pertanyaan) ini menyebabkan bias diabaikan. Properti kedua dan ketiga dicapai dengan menggunakan RNGCryptoServiceProvider
alih-alih 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);
}
Yang paling sederhana:
public static string GetRandomAlphaNumeric()
{
return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}
Anda bisa mendapatkan kinerja yang lebih baik jika Anda membuat kode char array dan mengandalkan System.Random
:
public static string GetRandomAlphaNumeric()
{
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
Jika Anda khawatir huruf-huruf bahasa Inggris dapat berubah sewaktu-waktu dan Anda mungkin kehilangan bisnis, maka Anda dapat menghindari pengkodean yang sulit, tetapi harus berkinerja sedikit lebih buruk (sebanding dengan Path.GetRandomFileName
pendekatan)
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);
}
Dua pendekatan terakhir terlihat lebih baik jika Anda dapat menjadikannya metode ekstensi secara System.Random
instan.
chars.Select
sangat jelek karena bergantung pada ukuran output paling banyak ukuran alfabet.
'a'.To('z')
pendekatan?
chars.Select()
.Ambil (n) `hanya berfungsi jika chars.Count >= n
. Memilih pada urutan yang sebenarnya tidak Anda gunakan agak tidak intuitif, terutama dengan batasan panjang implisit. Saya lebih suka menggunakan Enumerable.Range
atau Enumerable.Repeat
. 2) Pesan kesalahan "char akhir harus kurang dari start char" adalah cara yang salah bulat / hilang a not
.
chars.Count
adalah cara > n
. Saya juga tidak mendapatkan bagian yang tidak intuitif. Itu membuat semua penggunaan Take
tidak intuitif bukan? Saya tidak percaya itu. Terima kasih telah menunjukkan kesalahan ketik.
Hanya beberapa perbandingan kinerja dari berbagai jawaban di utas ini:
// 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);
}
Diuji dalam LinqPad. Untuk ukuran string 10, menghasilkan:
- dari Linq = chdgmevhcy [10]
- dari Loop = gtnoaryhxr [10]
- dari Select = rsndbztyby [10]
- dari GenerateRandomString = owyefjjakj [10]
- dari SecureFastRandom = VzougLYHYP [10]
- dari SecureFastRandom-NoCache = oVQXNGmO1S [10]
Dan angka kinerja cenderung sedikit berbeda, sangat kadang NonOptimized
- kadang sebenarnya lebih cepat, dan kadang ForLoop
- kadang dan GenerateRandomString
beralih siapa yang memimpin.
- LinqIsTheNewBlack (10000x) = 96762 kutu telah berlalu (9,672 ms)
- ForLoop (10000x) = 28970 ticks berlalu (2.897 ms)
- ForLoopNonOptimized (10000x) = 33336 ticks berlalu (3,3336 ms)
- Ulangi (10000x) = 78547 kutu yang berlalu (7.8547 ms)
- GenerateRandomString (10000x) = 27416 kutu telah lewat (2,7416 ms)
- SecureFastRandom (10000x) = 13176 ticks berlalu (5ms) terendah [Mesin berbeda]
- SecureFastRandom-NoCache (10000x) = 39541 tick telah berlalu (17ms) terendah [Berbeda mesin]
var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());
, di mana Anda ganti EachRandomizingMethod
dengan ... masing-masing metode
Satu baris kode Membership.GeneratePassword()
melakukan trik :)
Ini demo yang sama.
Kode yang ditulis oleh Eric J. cukup ceroboh (cukup jelas bahwa itu dari 6 tahun yang lalu ... dia mungkin tidak akan menulis kode itu hari ini), dan bahkan ada beberapa masalah.
Tidak seperti beberapa alternatif yang disajikan, yang satu ini secara kriptografi masuk akal.
Tidak Benar ... Ada bias dalam kata sandi (seperti yang ditulis dalam komentar), bcdefgh
sedikit lebih mungkin daripada yang lain ( a
bukan karena GetNonZeroBytes
tidak menghasilkan byte dengan nilai nol, jadi bias untuk a
itu diimbangi dengan itu), jadi itu tidak benar-benar terdengar kriptografis.
Ini harus memperbaiki semua masalah.
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);
}
}
Kami juga menggunakan string kustom acak tetapi kami implementasikan sebagai penolong string sehingga memberikan fleksibilitas ...
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();
}
Pemakaian
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();
atau
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
Kode satu baris sederhana saya berfungsi untuk saya :)
string random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));
Response.Write(random.ToUpper());
Response.Write(random.ToLower());
Untuk memperluas ini untuk string panjang apa pun
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);
}
Opsi lain bisa menggunakan Linq dan menggabungkan karakter acak ke stringbuilder.
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());
Pertanyaan: Mengapa saya harus membuang-buang waktu menggunakan Enumerable.Range
daripada mengetik "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;
}
}
Jawab: Senar ajaib itu BURUK. Apa ada yang memperhatikan?I
" tali saya di atas? Ibu saya mengajari saya untuk tidak menggunakan string sihir karena alasan ini ...
nb 1: Seperti yang dikatakan banyak orang seperti @dtb, jangan gunakan System.Random
jika Anda membutuhkan keamanan kriptografis ...
nb 2: Jawaban ini bukan yang paling efisien atau terpendek, tetapi saya ingin ruang untuk memisahkan jawaban dari pertanyaan. Tujuan dari jawaban saya lebih untuk memperingatkan terhadap string sihir daripada memberikan jawaban inovatif yang mewah.
I
?"
[A-Z0-9]
. Jika, secara tidak sengaja, string acak Anda hanya menutupi [A-HJ-Z0-9]
hasil tidak mencakup rentang yang diijinkan penuh, yang mungkin bermasalah.
I
. Apakah karena ada satu karakter yang kurang dan itu membuatnya lebih mudah untuk retak? Apa statistik pada kata sandi yang dapat dipecahkan yang berisi 35 karakter dalam kisaran dari 36. Saya pikir saya lebih suka mengambil risiko ... atau hanya membuktikan buluh rentang karakter ... daripada memasukkan semua sampah ekstra dalam kode saya. Tapi itu aku. Maksudku, bukan untuk menjadi lubang pantat, aku hanya mengatakan. Kadang-kadang saya pikir programmer cenderung menempuh rute ekstra-kompleks demi menjadi ekstra-kompleks.
I
dan O
dari jenis string acak ini untuk menghindari manusia membingungkan mereka dengan 1
dan 0
. Jika Anda tidak peduli tentang memiliki string yang dapat dibaca manusia, baik, tetapi jika itu adalah sesuatu yang seseorang perlu ketik, maka sebenarnya pintar untuk menghapus karakter tersebut.
Versi solusi DTB yang sedikit lebih bersih.
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
return string.Join("", list);
Preferensi gaya Anda dapat bervariasi.
Setelah meninjau jawaban lain dan mempertimbangkan komentar CodeInChaos, bersama dengan CodeInChaos masih bias (walaupun kurang) jawaban, saya pikir diperlukan solusi final cut and paste . Jadi, sementara memperbarui jawaban saya, saya memutuskan untuk keluar semua.
Untuk versi terbaru dari kode ini, silakan kunjungi repositori Hg baru di Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Saya sarankan Anda copy dan paste kode dari: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (make yakin Anda mengklik tombol Raw untuk membuatnya lebih mudah untuk menyalin dan memastikan Anda memiliki versi terbaru, saya pikir tautan ini menuju ke versi kode yang spesifik, bukan yang terbaru).
Catatan yang diperbarui:
Solusi akhir untuk pertanyaan:
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);
}
Tetapi Anda membutuhkan kelas baru saya (yang belum diuji):
/// <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
}
}
}
Untuk riwayat - solusi lama saya untuk jawaban ini, menggunakan objek acak:
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);
}
Kinerja:
Lihat juga:
Tautan ini adalah pendekatan lain. Buffering dapat ditambahkan ke basis kode baru ini, tetapi yang paling penting adalah mengeksplorasi berbagai pendekatan untuk menghilangkan bias, dan membandingkan kecepatan dan pro / kontra.
charSet.Length
bukan 62
. 2) Statis Random
tanpa penguncian berarti kode ini bukan threadsafe. 3) mengurangi 0-255 mod 62 memperkenalkan bias yang terdeteksi. 4) Anda tidak dapat menggunakan ToString
array char, yang selalu kembali "System.Char[]"
. Anda harus menggunakannya new String(rName)
sebagai gantinya.
System.Random
) dan kemudian dengan hati-hati menghindari bias dalam kode Anda sendiri. Ungkapan "memoles kotoran" muncul di pikiran.
Mengerikan, aku tahu, tapi aku tidak bisa menahan diri:
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);
}
}
}
Saya mencari jawaban yang lebih spesifik, di mana saya ingin mengontrol format string acak dan menemukan posting ini. Sebagai contoh: plat nomor (mobil) memiliki format tertentu (per negara) dan saya ingin membuat plat nomor acak.
Saya memutuskan untuk menulis metode ekstensi Acak saya sendiri untuk ini. (ini untuk menggunakan kembali objek acak yang sama, karena Anda bisa memiliki dua kali lipat dalam skenario multi-threading). Saya membuat intisari ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), tetapi juga akan menyalin kelas ekstensi di sini:
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)];
}
}
Sekarang dalam rasa satu-liner.
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
harus dibuang setelah digunakan.
Cobalah untuk menggabungkan dua bagian: unik (urutan, penghitung atau tanggal) dan acak
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;
}
}
Tes:
[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())
}
<=
dan >=
bukannya <
dan >
. 3) Saya akan menambahkan tanda kurung yang tidak perlu di sekitar &&
ekspresi untuk memperjelas bahwa mereka memiliki prioritas, tetapi tentu saja itu hanya pilihan gaya.
Solusi tanpa menggunakan Random
:
var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);
var randomStr = new string(chars.SelectMany(str => str)
.OrderBy(c => Guid.NewGuid())
.Take(8).ToArray());
Berikut ini adalah varian dari solusi Eric J, yaitu suara kriptografis, untuk WinRT (Aplikasi 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();
}
Jika kinerja penting (terutama ketika panjangnya tinggi):
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();
}
Saya tahu ini bukan cara terbaik. Tetapi Anda dapat mencoba ini.
string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
Saya tidak tahu bagaimana suara ini secara kriptografis, tetapi ini lebih mudah dibaca dan ringkas daripada solusi yang lebih rumit (imo), dan itu harus lebih "acak" daripada System.Random
solusi berbasis.
return alphabet
.OrderBy(c => Guid.NewGuid())
.Take(strLength)
.Aggregate(
new StringBuilder(),
(builder, c) => builder.Append(c))
.ToString();
Saya tidak dapat memutuskan apakah menurut saya versi ini atau yang berikutnya "lebih cantik", tetapi mereka memberikan hasil yang sama persis:
return new string(alphabet
.OrderBy(o => Guid.NewGuid())
.Take(strLength)
.ToArray());
Memang, itu tidak dioptimalkan untuk kecepatan, jadi jika itu penting untuk menghasilkan jutaan string acak setiap detik, coba yang lain!
CATATAN: Solusi ini tidak memungkinkan pengulangan simbol dalam alfabet, dan alfabet HARUS berukuran sama atau lebih besar dari string output, membuat pendekatan ini kurang diinginkan dalam beberapa keadaan, semuanya tergantung pada use-case Anda.
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
secara langsung? Menyulitkan kode dan tidak mencapai apa pun yang bermanfaat.
Berikut adalah mekanisme untuk menghasilkan string alpha-numeric acak (saya menggunakan ini untuk menghasilkan kata sandi dan menguji data) tanpa mendefinisikan alfabet dan angka,
CleanupBase64 akan menghapus bagian-bagian yang diperlukan dalam string dan terus menambahkan huruf alfanumerik acak secara rekursif.
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
dan melakukan panggilan keGetRandomString
dari dalam SanitiseBase64String
. Juga Anda telah menyatakan SanitiseBase64String
dan panggilan CleanUpBase64String
di GenerateRandomString
.
Ada satu paket nuget yang membuat hal ini sangat sederhana.
var myObject = new Faker<MyObject>()
.RuleFor(p => p.MyAlphaNumericProperty, f => f.Random.AlphaNumeric(/*lenght*/ 7))
.Generate();
Salah satu contoh yang baik ada di sini .
tidak 100% yakin, karena saya tidak menguji SETIAP opsi di sini, tetapi dari yang saya uji, ini adalah yang tercepat. mengatur waktu dengan stopwatch dan menunjukkan 9-10 kutu jadi jika kecepatan lebih penting daripada keamanan, coba ini:
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);
}
}