Bagaimana saya bisa menghasilkan string alfanumerik acak?

968

Bagaimana saya bisa menghasilkan string alfanumerik 8 karakter acak dalam C #?

KingNestor
sumber
2
Apa batasan jika ada yang Anda miliki pada set karakter? Hanya karakter bahasa Inggris dan 0-9? Kasing campuran?
Eric J.
9
Perhatikan bahwa Anda TIDAK boleh menggunakan metode apa pun berdasarkan Randomkelas untuk menghasilkan kata sandi. Penyemaian Randomentropi sangat rendah, sehingga tidak benar-benar aman. Gunakan PRNG kriptografi untuk kata sandi.
CodesInChaos
2
Akan menyenangkan untuk memasukkan lokalisasi bahasa dalam pertanyaan ini. Terutama jika gui Anda harus memenuhi kebutuhan orang Cina atau Bulgaria!
Peter Jamsmenson
15
Sesuatu dengan banyak peningkatan ini dan banyak jawaban berkualitas ini tidak layak untuk ditandai sebagai ditutup. Saya memilih itu dibuka kembali.
John Coleman

Jawaban:

1686

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 Randomkelas membuat ini tidak cocok untuk keamanan apa pun yang terkait , seperti membuat kata sandi atau token. Gunakan RNGCryptoServiceProviderkelas jika Anda memerlukan generator nomor acak yang kuat.)

dtb
sumber
24
@Alex: Saya sudah menjalankan beberapa tes cepat dan tampaknya skala cukup banyak linear ketika menghasilkan string lebih lama (asalkan sebenarnya ada cukup memori yang tersedia). Karena itu, jawaban Dan Rigby hampir dua kali lebih cepat dari yang ini di setiap tes.
LukeH
6
Baik. Jika kriteria Anda adalah bahwa ia menggunakan LINQ dan bahwa ia memiliki narasi kode buruk maka pasti lutut lebah. Baik narasi kode dan jalur aktual eksekusi agak tidak efisien dan tidak langsung. Jangan salah paham, saya seorang hipster kode besar (saya suka python), tapi ini mesin rberg goldberg.
eremzeit
5
Meskipun ini secara teknis menjawab pertanyaan, hasilnya sangat menyesatkan. Menghasilkan 8 karakter acak sepertinya ada sangat banyak hasil, sedangkan ini yang terbaik menghasilkan 2 miliar hasil yang berbeda. Dan dalam praktiknya bahkan lebih sedikit. Anda juga harus menambahkan peringatan FAT BESAR untuk tidak menggunakan ini untuk hal-hal terkait keamanan apa pun.
CodesInChaos
41
@xaisoft: Huruf kecil dibiarkan sebagai latihan untuk pembaca.
dtb
15
Baris berikut ini lebih banyak memori (dan dengan demikian waktu) lebih efisien daripada yang diberikanreturn new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
Tyson Williams
376
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 Randomkelas membuat ini tidak cocok untuk keamanan apa pun yang terkait , seperti membuat kata sandi atau token. Gunakan RNGCryptoServiceProviderkelas jika Anda memerlukan generator nomor acak yang kuat.)

Dan Rigby
sumber
4
@Alex: Ini bukan jawaban tercepat mutlak, tetapi itu adalah jawaban "nyata" tercepat (yaitu, dari mereka yang memungkinkan kontrol atas karakter yang digunakan dan panjang string).
LukeH
2
@Alex: Solusi Adam Porad GetRandomFileNamelebih cepat tetapi tidak memungkinkan kontrol karakter yang digunakan dan panjang maksimal yang mungkin adalah 11 karakter. GuidSolusi Douglas adalah kilat-cepat tetapi karakter dibatasi untuk A-F0-9 dan panjang maksimal yang mungkin adalah 32 karakter.
LukeH
1
@ Adam: Ya, Anda bisa menyetujui hasil dari beberapa panggilan, GetRandomFileNametetapi kemudian (a) Anda akan kehilangan keunggulan kinerja Anda, dan (b) kode Anda akan menjadi lebih rumit.
LukeH
2
@xaisoft buat instance dari objek Random () di luar loop Anda. Jika Anda membuat banyak instance Random () dalam interval pendek maka panggilan ke .Next () akan mengembalikan nilai yang sama seperti Random () menggunakan seed berdasarkan waktu.
Dan Rigby
2
@xaisoft Jangan gunakan jawaban ini untuk keamanan apa pun yang penting, seperti kata sandi. System.Randomtidak cocok untuk keamanan.
CodesInChaos
333

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")}%");
            }
        }
    }
}
Eric J.
sumber
11
Ini terlihat seperti pendekatan yang tepat untuk saya - kata sandi acak, garam, entropi dan sebagainya tidak boleh dihasilkan menggunakan Random () yang dioptimalkan untuk kecepatan dan menghasilkan urutan angka yang dapat direproduksi; RNGCryptoServiceProvider.GetNonZeroBytes () di sisi lain menghasilkan urutan angka liar yang TIDAK dapat direproduksi.
mindplay.dk
25
Huruf-hurufnya sedikit bias (255% 62! = 0). Terlepas dari kekurangan kecil ini, sejauh ini solusi terbaik di sini.
CodesInChaos
13
Perhatikan bahwa ini tidak terdengar jika Anda ingin kekuatan crypto, keacakan tidak bias. (Dan jika Anda tidak menginginkannya maka mengapa harus menggunakan RNGCSPpertama kali?) Menggunakan mod untuk mengindeks ke dalam charsarray berarti bahwa Anda akan mendapatkan output yang bias kecuali chars.Lengthkebetulan menjadi pembagi 256.
LukeH
15
Satu kemungkinan untuk mengurangi bias banyak, adalah meminta 4*maxSizebyte acak, lalu gunakan (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. Saya juga akan menggunakan GetBytesbukan GetNonZeroBytes. Dan akhirnya Anda dapat menghapus panggilan pertama ke GetNonZeroBytes. Anda tidak menggunakan hasilnya.
CodesInChaos
14
Fakta menyenangkan: AZ az 0-9 adalah 62 karakter. Orang-orang menunjukkan bias huruf karena 256% 62! = 0. ID video YouTube adalah AZ az 0-9, serta "-" dan "_", yang menghasilkan 64 karakter yang mungkin, yang dibagi menjadi 256 secara merata. Kebetulan? Saya pikir tidak! :)
qJake
200

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.

Douglas
sumber
22
Anda benar-benar membuat duplikat? Mengejutkan di 5.316.911.983.139.663.491.615.228.241.121.400.000 kemungkinan kombinasi GUID.
Alex
73
@Alex: Dia memperpendek GUID menjadi 8 karakter, sehingga kemungkinan tabrakan jauh lebih tinggi daripada GUID.
dtb
23
Tidak ada yang bisa menghargai ini selain kutu buku :) Ya Anda memang benar, batas 8 karakter membuat perbedaan.
Alex
31
Guid.NewGuid (). ToString ("n") akan menjaga tanda hubung keluar, tidak perlu panggilan Replace (). Tetapi harus disebutkan, GUID hanya 0-9 dan AF. Jumlah kombinasi "cukup baik," tetapi tidak mendekati apa yang diizinkan oleh string acak alfanumerik yang sebenarnya . Peluang tabrakan adalah 1: 4.294.967.296 - sama dengan bilangan bulat 32-bit acak.
richardtallent
32
1) GUID dirancang untuk menjadi unik, bukan acak. Sementara versi windows saat ini menghasilkan GUID V4 yang memang acak, itu tidak dijamin. Misalnya versi windows yang lebih lama menggunakan GUID V1, di mana Anda bisa gagal. 2) Hanya dengan menggunakan karakter hex mengurangi kualitas string acak secara signifikan. Dari 47 hingga 32 bit. 3) Orang-orang meremehkan probabilitas tabrakan, karena mereka memberikannya untuk pasangan individu. Jika Anda menghasilkan nilai 100k 32 bit, Anda mungkin memiliki satu tabrakan di antaranya. Lihat masalah Ulang Tahun.
CodesInChaos
72

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

Adam Porad
sumber
27
Ini bekerja dengan baik. Saya menjalankannya melalui 100.000 iterasi dan tidak pernah memiliki nama duplikat. Namun, saya memang menemukan beberapa kata vulgar (dalam bahasa Inggris). Bahkan tidak akan memikirkan hal ini kecuali salah satu yang pertama dalam daftar memiliki F *** di dalamnya. Hanya kepala jika Anda menggunakan ini untuk sesuatu yang akan dilihat pengguna.
techturtle
3
@turturtle Terima kasih atas peringatannya. Saya kira ada risiko untuk kata-kata vulgar dengan generasi string acak yang menggunakan semua huruf dalam alfabet.
Adam Porad
bagus dan sederhana tetapi tidak bagus untuk string panjang ... pilih trik yang bagus ini
Maher Abuthraa
Metode ini tampaknya hanya mengembalikan string alfanumerik huruf kecil.
jaybro
2
Ada kata-kata vulgar dari waktu ke waktu, tetapi jika Anda menjaga ini berjalan cukup lama pada akhirnya itu menulis Shakespeare. (Hanya beberapa masa kehidupan alam semesta. :)
Slothario
38

Tujuan utama kode saya adalah:

  1. Distribusi string hampir seragam (tidak peduli dengan penyimpangan kecil, asalkan kecil)
  2. Ini menghasilkan lebih dari beberapa miliar string untuk setiap set argumen. Menghasilkan string 8 karakter (~ 47 bit entropi) tidak ada artinya jika PRNG Anda hanya menghasilkan 2 miliar (31 bit entropi) nilai yang berbeda.
  3. Ini aman, karena saya berharap orang menggunakan ini untuk kata sandi atau token keamanan lainnya.

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 RNGCryptoServiceProvideralih-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);
}
CodesInChaos
sumber
1
Tidak ada persimpangan dengan 64 x Z dan Math.Pow (2, Y). Jadi, sementara membuat angka yang lebih besar mengurangi bias, itu tidak menghilangkannya. Saya memperbarui jawaban saya di bawah, pendekatan saya adalah membuang input acak dan mengganti dengan nilai lain.
Todd
@Todd Saya tahu itu tidak menghilangkan bias, tapi saya memilih kesederhanaan dari solusi ini daripada menghilangkan bias yang secara praktis tidak relevan.
CodesInChaos
Saya setuju untuk sebagian besar kasus itu mungkin praktis tidak relevan. Tapi sekarang saya sudah memperbarui milik saya menjadi secepat Acak dan sedikit lebih aman dari milik Anda. Semua sumber terbuka untuk semua orang untuk berbagi. Ya, saya terlalu banyak membuang waktu dalam hal ini ...
Todd
Jika kita menggunakan penyedia RNG apakah kita punya cara untuk menghindari bias secara teori? Saya tidak yakin ... Jika Todd memaksudkan cara ketika dia menghasilkan angka acak tambahan (ketika kita berada di zona bias) maka itu bisa menjadi asumsi yang salah. RNG memiliki distribusi hampir linier dari semua nilai yang dihasilkan dalam rata-rata. Tetapi itu tidak berarti bahwa kita tidak akan memiliki korelasi lokal antara byte yang dihasilkan. Jadi byte tambahan hanya untuk zona bias masih dapat memberi kita beberapa bias tetapi karena alasan yang berbeda. Kemungkinan besar bias ini akan sangat kecil. TETAPI dalam hal ini peningkatan total byte yang dihasilkan lebih mudah.
Maxim
1
@ Maxim Anda dapat menggunakan penolakan untuk sepenuhnya menghilangkan bias (dengan asumsi generator yang mendasari adalah acak sempurna). Sebagai gantinya kode mungkin berjalan lama sewenang-wenang (dengan probabilitas kecil secara eksponensial).
CodesInChaos
32

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.GetRandomFileNamependekatan)

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.Randominstan.

nawfal
sumber
1
Penggunaannya chars.Selectsangat jelek karena bergantung pada ukuran output paling banyak ukuran alfabet.
CodesInChaos
@CodesInChaos Saya tidak yakin apakah saya mengerti Anda. Maksud Anda dalam 'a'.To('z')pendekatan?
nawfal
1
1) 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.Rangeatau Enumerable.Repeat. 2) Pesan kesalahan "char akhir harus kurang dari start char" adalah cara yang salah bulat / hilang a not.
CodesInChaos
@ CodeInChaos tetapi dalam kasus saya chars.Countadalah cara > n. Saya juga tidak mendapatkan bagian yang tidak intuitif. Itu membuat semua penggunaan Taketidak intuitif bukan? Saya tidak percaya itu. Terima kasih telah menunjukkan kesalahan ketik.
nawfal
4
Ini ditampilkan di theDailyWTF.com sebagai artikel CodeSOD.
22

Hanya beberapa perbandingan kinerja dari berbagai jawaban di utas ini:

Metode & Pengaturan

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

Hasil

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 GenerateRandomStringberalih 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]
drzaus
sumber
3
Akan menarik untuk mengetahui mana yang dibuat dupes.
Rebecca
@Junto - untuk mencari tahu yang menghasilkan duplikat, seperti var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, di mana Anda ganti EachRandomizingMethoddengan ... masing-masing metode
drzaus
13

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), bcdefghsedikit lebih mungkin daripada yang lain ( abukan karena GetNonZeroBytestidak menghasilkan byte dengan nilai nol, jadi bias untuk aitu 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);
    }
}
xanatos
sumber
7

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);
Tuan Pumpkin
sumber
7

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);
    }
Raj kumar
sumber
Saya suka metode panduan juga - terasa sangat ringan
ozzy432836
6

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());
AAD
sumber
6

Pertanyaan: Mengapa saya harus membuang-buang waktu menggunakan Enumerable.Rangedaripada 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.Randomjika 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.

Wai Ha Lee
sumber
Mengapa saya peduli bahwa tidak ada " I?"
Christine
1
Alfanumerik (kasus abaikan) adalah [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.
Wai Ha Lee
Bagaimana itu bisa menjadi masalah? Jadi itu tidak mengandung 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.
Christine
1
Itu tergantung pada use case. Sangat umum untuk mengecualikan karakter seperti Idan Odari jenis string acak ini untuk menghindari manusia membingungkan mereka dengan 1dan 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.
Chris Pratt
5

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.

Rob Deary
sumber
Ini jauh lebih baik dan lebih efisien daripada jawaban yang diterima.
Wedge
5
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }
Tejas
sumber
5

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:

  1. Berkaitan dengan beberapa jawaban lain - Jika Anda mengetahui panjang output, Anda tidak memerlukan StringBuilder, dan saat menggunakan ToCharArray, ini membuat dan mengisi array (Anda tidak perlu membuat array kosong terlebih dahulu)
  2. Berkaitan dengan beberapa jawaban lain - Anda harus menggunakan NextBytes, daripada mendapatkan satu per satu untuk kinerja
  3. Secara teknis Anda dapat menyematkan array byte untuk akses yang lebih cepat .. biasanya layak jika iterasi Anda lebih dari 6-8 kali melalui array byte. (Tidak dilakukan di sini)
  4. Penggunaan RNGCryptoServiceProvider untuk keacakan terbaik
  5. Penggunaan caching buffer data acak 1MB - pembandingan menunjukkan kecepatan akses byte tunggal yang di-cache ~ 1000x lebih cepat - mengambil 9ms lebih dari 1MB vs 989ms untuk tidak di-cache.
  6. Penolakan zona bias dioptimalkan di dalam kelas baru saya.

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:

  1. SecureFastRandom - Jalankan tunggal pertama = ~ 9-33ms . Tak kelihatan. Sedang berjalan : 5 ms (kadang-kadang naik hingga 13 ms) lebih dari 10.000 iterasi, Dengan iterasi rata-rata tunggal = 1,5 mikrodetik. . Catatan: Membutuhkan umumnya 2, tetapi kadang-kadang hingga 8 cache menyegarkan - tergantung pada berapa banyak byte tunggal melebihi zona bias
  2. Acak - Menjalankan tunggal pertama = ~ 0-1ms . Tak kelihatan. Sedang berlangsung : 5ms lebih dari 10.000 iterasi. Dengan iterasi rata-rata tunggal = 0,5 mikrodetik. . Tentang kecepatan yang sama.

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.

Merari Schroeder
sumber
Saya menemukan beberapa peningkatan kinerja sedikit untuk metode Anda, yang tampaknya berpuasa banyak - stackoverflow.com/a/17092645/1037948
drzaus
5
1) Mengapa semua konstanta ajaib itu? Anda menetapkan panjang output tiga kali. Cukup definisikan sebagai konstanta atau parameter. Anda bisa menggunakan charSet.Lengthbukan 62. 2) Statis Randomtanpa penguncian berarti kode ini bukan threadsafe. 3) mengurangi 0-255 mod 62 memperkenalkan bias yang terdeteksi. 4) Anda tidak dapat menggunakan ToStringarray char, yang selalu kembali "System.Char[]". Anda harus menggunakannya new String(rName)sebagai gantinya.
CodesInChaos
Terima kasih @CodesInChaos, saya tidak pernah memikirkan hal-hal itu saat itu. Masih hanya menggunakan kelas acak, tetapi ini harus lebih baik. Saya tidak bisa memikirkan cara yang lebih baik untuk mendeteksi dan mengoreksi input bias.
Todd
Agak konyol untuk memulai dengan RNG yang lemah ( System.Random) dan kemudian dengan hati-hati menghindari bias dalam kode Anda sendiri. Ungkapan "memoles kotoran" muncul di pikiran.
CodesInChaos
@CodesInChaos Dan sekarang murid magang telah melampaui tuannya
Todd
4

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

james
sumber
4

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)];
    }
}
Sam Vanhoutte
sumber
4

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());
}
Matas Vaitkevicius
sumber
2
Menggunakan properti untuk sesuatu yang berubah pada setiap akses agak meragukan. Saya akan merekomendasikan menggunakan metode sebagai gantinya.
CodesInChaos
2
RNGCryptoServiceProviderharus dibuang setelah digunakan.
Tsahi Asher
Saya memperbaiki masalah IDisposable, tetapi ini masih sangat meragukan, membuat RNGCryptoServiceProvider baru untuk setiap surat.
piojo
@CodesInChaos selesai, sekarang menjadi metode.
Matas Vaitkevicius
3

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())
    }
RouR
sumber
1) Anda dapat menggunakan literal karakter alih-alih nilai ASCII yang terkait dengan karakter tersebut. 2) Anda memiliki kesalahan satu-per-satu dalam kode pencocokan interval Anda. Anda harus menggunakan <=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.
CodesInChaos
+1 Baik untuk menghapus bias dan menambahkan pengujian. Saya tidak yakin mengapa Anda menambahkan string acak Anda dengan string yang berasal dari timestamp? Selain itu, Anda masih harus membuang RNGCryptoServiceProvider Anda
monty
2

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());
wb
sumber
2
NewGuid menggunakan acak secara internal. Jadi ini masih menggunakan acak, hanya menyembunyikannya.
Wedge
2

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();
}
huyc
sumber
1
Ini tidak terdengar secara kriptografis. Ada bias kecil karena operasi modulus tidak menyebarkan seluruh lebar ulong secara merata menjadi 62 karakter.
Lie Ryan
1

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);
Sagar
sumber
2
Bagaimana itu satu baris? Console.WriteLine ($ "String acak: {Path.GetRandomFileName (). Ganti (". "," "")} "); adalah satu baris.
PmanAce
1

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.Randomsolusi 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.

sara
sumber
0

Jika nilai-nilai Anda tidak sepenuhnya acak, tetapi sebenarnya mungkin tergantung pada sesuatu - Anda dapat menghitung hash md5 atau sha1 dari 'somwthing' dan kemudian memotongnya dengan panjang berapa pun yang Anda inginkan.

Anda juga dapat menghasilkan dan memotong panduan.

Alexey B.
sumber
0
public static class StringHelper
{
    private static readonly Random random = new Random();

    private const int randomSymbolsDefaultCount = 8;
    private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private static int randomSymbolsIndex = 0;

    public static string GetRandomSymbols()
    {
        return GetRandomSymbols(randomSymbolsDefaultCount);
    }

    public static string GetRandomSymbols(int count)
    {
        var index = randomSymbolsIndex;
        var result = new string(
            Enumerable.Repeat(availableChars, count)
                      .Select(s => {
                          index += random.Next(s.Length);
                          if (index >= s.Length)
                              index -= s.Length;
                          return s[index];
                      })
                      .ToArray());
        randomSymbolsIndex = index;
        return result;
    }
}
KregHEk
sumber
2
1) metode statis harus aman thread. 2) Apa gunanya menambah indeks daripada menggunakan hasil random.Nextsecara langsung? Menyulitkan kode dan tidak mencapai apa pun yang bermanfaat.
CodesInChaos
0

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);
        }
Dhanuka777
sumber
Anda telah menyatakan GenerateRandomStringdan melakukan panggilan keGetRandomString dari dalam SanitiseBase64String. Juga Anda telah menyatakan SanitiseBase64String dan panggilan CleanUpBase64Stringdi GenerateRandomString.
Wai Ha Lee
0

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 .

nzrytmn
sumber
0

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);
              }
     }
PetNoire
sumber
Mengapa membalas? Pertanyaan ini telah dijawab berkali-kali namun Anda masih memposting jawaban yang belum berfungsi ..
Laurent