Làm thế nào tôi có thể tạo các chuỗi chữ và số ngẫu nhiên?


967

Làm cách nào để tạo chuỗi ký tự gồm 8 ký tự ngẫu nhiên trong C #?


2
Những hạn chế nếu bạn có trên bộ ký tự? Chỉ cần các ký tự tiếng Anh và 0-9? Trường hợp hỗn hợp?
Eric J.


9
Lưu ý rằng bạn KHÔNG nên sử dụng bất kỳ phương thức nào dựa trên Randomlớp để tạo mật khẩu. Việc gieo hạt Randomcó entropy rất thấp, vì vậy nó không thực sự an toàn. Sử dụng PRNG mật mã cho mật khẩu.
CodeInChaos

2
Sẽ rất tốt nếu bao gồm nội địa hóa ngôn ngữ trong câu hỏi này. Đặc biệt là nếu gui của bạn cần phục vụ cho tiếng Trung hoặc tiếng Bulgaria!
Peter Jamsmenson

15
Một cái gì đó với nhiều upvote và nhiều câu trả lời chất lượng này không xứng đáng được đánh dấu là đóng. Tôi bỏ phiếu rằng nó sẽ được mở lại.
John Coleman

Câu trả lời:


1684

Tôi nghe nói LINQ là màu đen mới, vì vậy đây là nỗ lực của tôi khi sử dụng 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());
}

(Lưu ý: Việc sử dụng Randomlớp làm cho điều này không phù hợp với bất kỳ điều gì liên quan đến bảo mật , chẳng hạn như tạo mật khẩu hoặc mã thông báo. Sử dụng RNGCryptoServiceProviderlớp nếu bạn cần một trình tạo số ngẫu nhiên mạnh.)


24
@Alex: Tôi đã chạy một vài thử nghiệm nhanh và dường như quy mô khá tuyến tính khi tạo chuỗi dài hơn (miễn là thực sự có đủ bộ nhớ). Phải nói rằng, câu trả lời của Dan Rigby nhanh gấp đôi câu hỏi này trong mọi bài kiểm tra.
LukeH

6
Tốt. Nếu tiêu chí của bạn là nó sử dụng linq và nó có một tường thuật mã tệ hại thì đó chắc chắn là đầu gối của con ong. Cả tường thuật mã và đường dẫn thực thi là không hiệu quả và gián tiếp. Đừng hiểu sai ý tôi, tôi là một hipster mã lớn (tôi yêu trăn), nhưng đây gần như là một cỗ máy vàng rube.
eremzeit

5
Trong khi kỹ thuật này trả lời câu hỏi, đầu ra của nó rất sai lệch. Tạo 8 ký tự ngẫu nhiên nghe có vẻ có rất nhiều kết quả, trong khi điều này tốt nhất tạo ra 2 tỷ kết quả khác nhau. Và trong thực tế thậm chí còn ít hơn. Bạn cũng nên thêm một cảnh báo BIG FAT để không sử dụng điều này cho bất kỳ thứ gì liên quan đến bảo mật.
CodeInChaos 17/03/2016

41
@xaisoft: Chữ thường được để lại như một bài tập cho người đọc.
dtb

15
Dòng sau đây có nhiều bộ nhớ (và do đó thời gian) hiệu quả hơn so với dòng đã choreturn 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);

Không thanh lịch như giải pháp Linq.

(Lưu ý: Việc sử dụng Randomlớp làm cho điều này không phù hợp với bất kỳ điều gì liên quan đến bảo mật , chẳng hạn như tạo mật khẩu hoặc mã thông báo. Sử dụng RNGCryptoServiceProviderlớp nếu bạn cần một trình tạo số ngẫu nhiên mạnh.)


4
@Alex: Đây không phải là câu trả lời nhanh nhất tuyệt đối, nhưng nó là câu trả lời "thực" nhanh nhất (nghĩa là câu trả lời cho phép kiểm soát các ký tự được sử dụng và độ dài của chuỗi).
LukeH

2
@Alex: GetRandomFileNameGiải pháp của Adam Porad nhanh hơn nhưng không cho phép kiểm soát các ký tự được sử dụng và độ dài tối đa có thể là 11 ký tự. GuidGiải pháp của Douglas rất nhanh nhưng các ký tự được giới hạn ở A-F0-9 và độ dài tối đa có thể là 32 ký tự.
LukeH

1
@Adam: Có, bạn có thể kết quả nhiều cuộc gọi đến GetRandomFileNamenhưng sau đó (a) bạn sẽ mất lợi thế về hiệu suất và (b) mã của bạn sẽ trở nên phức tạp hơn.
LukeH

2
@xaisoft tạo phiên bản đối tượng Random () bên ngoài vòng lặp của bạn. Nếu bạn tạo nhiều phiên bản Random () trong một khoảng thời gian ngắn thì lệnh gọi đến .Next () sẽ trả về cùng giá trị như Random () sử dụng hạt giống dựa trên thời gian.
Dan Rigby

2
@xaisoft Đừng sử dụng câu trả lời này cho bất kỳ điều gì bảo mật quan trọng, như mật khẩu. System.Randomkhông phù hợp với an ninh.
CodeInChaos

333

CẬP NHẬT dựa trên ý kiến. Việc thực hiện ban đầu tạo ra ah ~ 1,95% thời gian và các ký tự còn lại ~ 1,56% thời gian. Bản cập nhật tạo ra tất cả các ký tự ~ 1,61% thời gian.

H SUP TRỢ FRAMEWORK - .NET Core 3 (và các nền tảng tương lai hỗ trợ .NET Standard 2.1 trở lên) cung cấp phương pháp âm thanh mã hóa RandomNumberGenerator.GetInt32 () để tạo số nguyên ngẫu nhiên trong phạm vi mong muốn.

Không giống như một số lựa chọn thay thế được trình bày, cái này là âm thanh mật mã .

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

Dựa trên một cuộc thảo luận về các lựa chọn thay thế ở đây và cập nhật / sửa đổi dựa trên các ý kiến ​​dưới đây.

Đây là một khai thác thử nghiệm nhỏ thể hiện sự phân phối các ký tự trong đầu ra cũ và được cập nhật. Để thảo luận sâu về phân tích tính ngẫu nhiên , hãy xem 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")}%");
            }
        }
    }
}

11
Điều này trông giống như cách tiếp cận chính xác với tôi - mật khẩu ngẫu nhiên, muối, entropy và vv không nên được tạo bằng Random () được tối ưu hóa cho tốc độ và tạo ra các chuỗi số có thể lặp lại; Mặt khác, RNGCryptoServiceProvider.GetNonZeroBytes () tạo ra các chuỗi số không có khả năng tái tạo.
mindplay.dk

25
Các chữ cái hơi thiên vị (255% 62! = 0). Mặc dù lỗ hổng nhỏ này, cho đến nay nó là giải pháp tốt nhất ở đây.
CodesInChaos

13
Lưu ý rằng đây không phải là âm thanh nếu bạn muốn độ mạnh của tiền điện tử, tính ngẫu nhiên không thiên vị. (Và nếu bạn không muốn điều đó thì sử dụng lý do tại sao RNGCSPở nơi đầu tiên?) Sử dụng mod để chỉ mục vào charsphương tiện mảng mà bạn sẽ nhận được thiên vị đầu ra trừ khi chars.Lengthxảy ra là một ước của 256.
LukeH

15
Một khả năng để giảm độ lệch rất nhiều, là yêu cầu 4*maxSizebyte ngẫu nhiên, sau đó sử dụng (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. Tôi cũng sẽ sử dụng GetBytesthay vì GetNonZeroBytes. Và cuối cùng bạn có thể loại bỏ cuộc gọi đầu tiên đến GetNonZeroBytes. Bạn không sử dụng kết quả của nó.
CodeInChaos

14
Sự thật thú vị: AZ az 0-9 là 62 ký tự. Mọi người đang chỉ ra độ lệch chữ cái vì 256% 62! = 0. ID video của YouTube là AZ az 0-9, cũng như "-" và "_", tạo ra 64 ký tự có thể, chia thành 256 ký tự. Sự trùng hợp? Tôi nghĩ là không! :)
qJake

200

Giải pháp 1 - 'phạm vi' lớn nhất với độ dài linh hoạt nhất

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

Giải pháp này có phạm vi rộng hơn so với sử dụng GUID vì GUID có một vài bit cố định luôn giống nhau và do đó không ngẫu nhiên, ví dụ 13 ký tự trong hex luôn là "4" - ít nhất là trong phiên bản 6 GUID.

Giải pháp này cũng cho phép bạn tạo ra một chuỗi có độ dài bất kỳ.

Giải pháp 2 - Một dòng mã - tốt cho tối đa 22 ký tự

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);

Bạn không thể tạo các chuỗi miễn là Giải pháp 1 và chuỗi không có cùng phạm vi do các bit cố định trong GUID, nhưng trong nhiều trường hợp, điều này sẽ thực hiện công việc.

Giải pháp 3 - Mã ít hơn một chút

Guid.NewGuid().ToString("n").Substring(0, 8);

Chủ yếu là giữ điều này ở đây cho mục đích lịch sử. Nó sử dụng mã ít hơn một chút, mặc dù chi phí có ít phạm vi hơn - bởi vì nó sử dụng hex thay vì base64, nó cần nhiều ký tự hơn để biểu thị cùng một phạm vi so với các giải pháp khác.

Điều đó có nghĩa là có nhiều cơ hội va chạm hơn - kiểm tra nó với 100.000 lần lặp của 8 chuỗi ký tự được tạo ra một bản sao.


22
Bạn thực sự tạo ra một bản sao? Đáng ngạc nhiên ở mức 5.316.911.983.139.663.491.615.228.241.121.400.000 kết hợp có thể có của GUID.
Alex

73
@Alex: Anh ấy rút ngắn GUID xuống còn 8 ký tự, vì vậy xác suất va chạm cao hơn nhiều so với GUID.
dtb

23
Không ai có thể đánh giá cao điều này ngoài mọt sách :) Có bạn hoàn toàn đúng, giới hạn 8 char tạo ra sự khác biệt.
Alex

31
Guid.NewGuid (). ToString ("n") sẽ giữ dấu gạch ngang, không cần cuộc gọi Thay thế (). Nhưng cần phải đề cập, GUID chỉ là 0-9 và AF. Số lượng kết hợp là "đủ tốt", nhưng không giống với những gì một chuỗi ngẫu nhiên chữ và số thực sự cho phép. Cơ hội va chạm là 1: 4.294.967.296 - giống như số nguyên 32 bit ngẫu nhiên.
richardtallent

32
1) GUID được thiết kế độc đáo, không ngẫu nhiên. Mặc dù các phiên bản hiện tại của windows tạo VID GUID thực sự ngẫu nhiên, nhưng điều đó không được đảm bảo. Ví dụ, các phiên bản cũ hơn của windows sử dụng V1 GUID, trong đó bạn có thể thất bại. 2) Chỉ cần sử dụng các ký tự hex sẽ làm giảm đáng kể chất lượng của chuỗi ngẫu nhiên. Từ 47 đến 32 bit. 3) Mọi người đang đánh giá thấp xác suất va chạm, vì họ đưa ra cho các cặp riêng lẻ. Nếu bạn tạo các giá trị 100k 32 bit, có thể bạn có một xung đột giữa chúng. Xem vấn đề sinh nhật.
CodeInChaos

72

Đây là một ví dụ mà tôi đã đánh cắp từ ví dụ Sam Allen tại Dot Net Perls

Nếu bạn chỉ cần 8 ký tự, thì hãy sử dụng Path.GetRandomFileName () trong không gian tên System.IO. Sam nói rằng việc sử dụng phương thức "Path.GetRandomFileName ở đây đôi khi vượt trội hơn, bởi vì nó sử dụng RNGCryptoServiceProvider để có tính ngẫu nhiên tốt hơn. Tuy nhiên, nó bị giới hạn ở 11 ký tự ngẫu nhiên."

GetRandomFileName luôn trả về chuỗi 12 ký tự với khoảng thời gian ở ký tự thứ 9. Vì vậy, bạn sẽ cần phải loại bỏ khoảng thời gian (vì đó không phải là ngẫu nhiên) và sau đó lấy 8 ký tự từ chuỗi. Trên thực tế, bạn chỉ có thể lấy 8 ký tự đầu tiên và không phải lo lắng về thời gian.

public string Get8CharacterRandomString()
{
    string path = Path.GetRandomFileName();
    path = path.Replace(".", ""); // Remove period.
    return path.Substring(0, 8);  // Return 8 character string
}

Tái bút: cảm ơn Sam


27
Điều này hoạt động tốt. Tôi đã chạy nó qua 100.000 lần lặp và không bao giờ có tên trùng lặp. Tuy nhiên, tôi đã tìm thấy một số từ thô tục (bằng tiếng Anh). Thậm chí sẽ không nghĩ về điều này ngoại trừ một trong những người đầu tiên trong danh sách có F *** trong đó. Chỉ cần ngẩng cao đầu nếu bạn sử dụng cái này cho một cái gì đó người dùng sẽ thấy.
techturtle

3
@techturtle Cảm ơn bạn đã cảnh báo. Tôi cho rằng có nguy cơ cho các từ thô tục với bất kỳ thế hệ chuỗi ngẫu nhiên nào sử dụng tất cả các chữ cái trong bảng chữ cái.
Adam Porad

đẹp và đơn giản nhưng không tốt cho chuỗi dài ... bỏ phiếu cho thủ thuật hay này
Maher Abuthraa

Phương pháp này dường như chỉ trả về các chuỗi ký tự chữ và số thường.
jaybro

2
Thỉnh thoảng có những từ thô tục, nhưng nếu bạn giữ nó chạy đủ lâu thì cuối cùng nó cũng viết Shakespeare. (Chỉ là một vài kiếp sống của vũ trụ. :)
Slothario

38

Các mục tiêu chính của mã của tôi là:

  1. Sự phân bố chuỗi gần như thống nhất (không quan tâm đến những sai lệch nhỏ, miễn là chúng nhỏ)
  2. Nó xuất ra hơn một vài tỷ chuỗi cho mỗi bộ đối số. Tạo một chuỗi 8 ký tự (~ 47 bit entropy) là vô nghĩa nếu PRNG của bạn chỉ tạo ra 2 tỷ (31 bit entropy) các giá trị khác nhau.
  3. Nó an toàn, vì tôi mong mọi người sử dụng mật khẩu này hoặc mật khẩu bảo mật khác.

Thuộc tính đầu tiên đạt được bằng cách lấy modulo giá trị 64 bit kích thước bảng chữ cái. Đối với các bảng chữ cái nhỏ (chẳng hạn như 62 ký tự trong câu hỏi), điều này dẫn đến sai lệch không đáng kể. Tài sản thứ hai và thứ ba đạt được bằng cách sử dụng RNGCryptoServiceProviderthay vì 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);
}

1
Không có giao điểm với 64 x Z và Math.Pow (2, Y). Vì vậy, trong khi tạo ra số lượng lớn hơn làm giảm sự thiên vị, nó không loại bỏ nó. Tôi đã cập nhật câu trả lời của mình xuống bên dưới, cách tiếp cận của tôi là loại bỏ các đầu vào ngẫu nhiên và thay thế bằng một giá trị khác.
Todd

@Todd Tôi biết rằng nó không loại bỏ sự thiên vị, nhưng tôi đã chọn sự đơn giản của giải pháp này hơn là loại bỏ sự thiên vị thực tế không liên quan.
CodeInChaos

Tôi đồng ý cho hầu hết các trường hợp có lẽ thực tế không liên quan. Nhưng bây giờ tôi đã cập nhật của tôi để nhanh như Ngẫu nhiên và an toàn hơn một chút so với của bạn. Tất cả nguồn mở cho tất cả để chia sẻ. Phải, tôi đã lãng phí quá nhiều thời gian cho việc này ...
Todd

Nếu chúng ta đang sử dụng nhà cung cấp RNG, chúng ta có cách nào để tránh sự thiên vị trong lý thuyết không? Tôi không chắc chắn ... Nếu Todd có nghĩa là khi anh ta tạo thêm số ngẫu nhiên (khi chúng ta ở trong vùng thiên vị) thì đó có thể là giả định sai. RNG có phân phối gần như tuyến tính của tất cả các giá trị được tạo trung bình. Nhưng điều đó không có nghĩa là chúng ta sẽ không có mối tương quan cục bộ giữa các byte được tạo. Vì vậy, byte bổ sung chỉ cho vùng thiên vị vẫn có thể cung cấp cho chúng tôi một số sai lệch nhưng do lý do khác nhau. Nhiều khả năng sự thiên vị này sẽ rất nhỏ. NHƯNG trong trường hợp này tăng tổng số byte được tạo ra là cách đơn giản hơn.
Maxim

1
@Maxim Bạn có thể sử dụng từ chối để loại bỏ hoàn toàn sai lệch (giả sử trình tạo bên dưới là hoàn toàn ngẫu nhiên). Đổi lại, mã có thể chạy dài tùy ý (với xác suất nhỏ theo cấp số nhân).
CodeInChaos

32

Điều đơn giản nhất:

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

Bạn có thể có được hiệu suất tốt hơn nếu bạn mã hóa mảng char và dựa vào System.Random:

public static string GetRandomAlphaNumeric()
{
    var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

Nếu bạn lo lắng bảng chữ cái tiếng Anh đôi khi có thể thay đổi và bạn có thể mất việc kinh doanh, thì bạn có thể tránh mã hóa cứng, nhưng sẽ hoạt động kém hơn một chút (có thể so sánh với Path.GetRandomFileNamecách tiếp cận)

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

Hai cách tiếp cận cuối cùng có vẻ tốt hơn nếu bạn có thể biến chúng thành một phương thức mở rộng System.Random.


1
Sử dụng chars.Selectlà một điều xấu xí lớn vì nó phụ thuộc vào kích thước đầu ra nhiều nhất là kích thước bảng chữ cái.
CodeInChaos

@CodesInChaos Tôi không chắc là tôi có hiểu bạn không. Ý bạn là trong 'a'.To('z')cách tiếp cận?
nawfal

1
1) chars.Select().Take (n) `chỉ hoạt động nếu chars.Count >= n. Chọn một chuỗi bạn không thực sự sử dụng là một chút không trực quan, đặc biệt là với ràng buộc độ dài ngầm định đó. Tôi muốn sử dụng Enumerable.Rangehoặc Enumerable.Repeat. 2) Thông báo lỗi "char kết thúc phải nhỏ hơn start char" là cách sai vòng / thiếu a not.
CodeInChaos

@CodesInChaos nhưng trong trường hợp của tôi chars.Countlà cách > n. Ngoài ra tôi không nhận được phần không trực quan. Điều đó làm cho tất cả sử dụng Takekhông trực quan phải không? Tôi không tin điều đó Cảm ơn đã chỉ lỗi đánh máy.
nawfal

4
Điều này được giới thiệu trên trang DailyDTF.com dưới dạng bài viết của CodeSOD.

22

Chỉ cần một số so sánh hiệu suất của các câu trả lời khác nhau trong chủ đề này:

Phương pháp & Thiết lập

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

Các kết quả

Đã thử nghiệm trong LinqPad. Đối với kích thước chuỗi là 10, tạo:

  • từ Linq = chdgmevhcy [10]
  • từ Vòng lặp = gtnoaryhxr [10]
  • từ Chọn = rsndbztyby [10]
  • từ GenerateRandomString = owyefjjakj [10]
  • từ SecureFastRandom = VzougLYHYP [10]
  • từ SecureFastRandom-NoCache = oVQXNGmO1S [10]

Và số hiệu suất có xu hướng thay đổi một chút, đôi khi NonOptimizedthực sự nhanh hơn, và đôi khi ForLoopGenerateRandomStringchuyển đổi người dẫn đầu.

  • LinqIsTheNewBlack (10000x) = 96762 tick đã trôi qua (9.6762 ms)
  • ForLoop (10000x) = 28970 tick đã trôi qua (2.897 ms)
  • ForLoopNonOptimized (10000x) = 33336 tick đã trôi qua (3.3336 ms)
  • Lặp lại (10000x) = 78547 tick đã trôi qua (7,8547 ms)
  • GenerateRandomString (10000x) = 27416 tick đã trôi qua (2,7416 ms)
  • SecureFastRandom (10000x) = 13176 tick trôi qua (5ms) thấp nhất [Máy ​​khác nhau]
  • SecureFastRandom-NoCache (10000x) = 39541 tick trôi qua (17ms) thấp nhất [Máy ​​khác nhau]

3
Sẽ rất thú vị để biết những cái nào tạo ra bản sao.
Rebecca

@Junto - để tìm ra kết quả nào trùng lặp, giống như var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, nơi bạn thay thế EachRandomizingMethodbằng ... mỗi phương thức
drzaus

19

Một dòng mã Membership.GeneratePassword()thực hiện mánh khóe :)

Đây là một bản demo cho cùng.


1
Trông giống như Microsoft chuyển vào liên kết .. khác mẫu mã là tại msdn.microsoft.com/en-us/library/ms152017 hoặc aspnet.4guysfromrolla.com/demos/GeneratePassword.aspx hoặc developer.xamarin.com/api/member/...
Pooran

Tôi đã nghĩ về điều đó nhưng không thể loại bỏ các ký tự không chữ và số vì đối số thứ 2 là các ký tự không phải là TỐI THIỂU
ozzy432836

13

Mã được viết bởi Eric J. khá cẩu thả (khá rõ ràng là từ 6 năm trước ... có lẽ anh ta sẽ không viết mã đó ngày hôm nay), và thậm chí còn có một số vấn đề.

Không giống như một số lựa chọn thay thế được trình bày, cái này là âm thanh mật mã.

Không đúng ... Có một sai lệch trong mật khẩu (như được viết trong một bình luận), bcdefghcó thể xảy ra hơn một chút so với các mật khẩu khác ( akhông phải bởi vì GetNonZeroBytesnó không tạo ra các byte có giá trị bằng 0, vì vậy độ lệch đối với anó được cân bằng bởi nó), vì vậy nó không thực sự là âm thanh mật mã.

Điều này sẽ sửa tất cả các vấn đề.

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

7

Chúng tôi cũng sử dụng ngẫu nhiên chuỗi tùy chỉnh nhưng chúng tôi đã triển khai với tư cách là người trợ giúp của chuỗi để cung cấp sự linh hoạt ...

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

Sử dụng

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

hoặc là

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);

7

Mã một dòng đơn giản của tôi làm việc cho tôi :)

string  random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));

Response.Write(random.ToUpper());
Response.Write(random.ToLower());

Để mở rộng về điều này cho bất kỳ chuỗi độ dài

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

Tôi cũng thích phương pháp hướng dẫn - cảm thấy thực sự nhẹ nhàng
ozzy432836

6

Một lựa chọn khác có thể là sử dụng Linq và tổng hợp các ký tự ngẫu nhiên thành một chuỗi xây dựng.

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

6

Câu hỏi: Tại sao tôi nên lãng phí thời gian sử dụng Enumerable.Rangethay vì gõ vào "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;
    }
}

Trả lời: Chuỗi ma thuật là BAD. Có ai nhận thấy không có " I" trong chuỗi của tôi ở đầu không? Mẹ tôi dạy tôi không sử dụng dây ma thuật vì lý do này ...

nb 1: Như nhiều người khác như @dtb đã nói, đừng sử dụng System.Randomnếu bạn cần bảo mật bằng mật mã ...

nb 2: Câu trả lời này không hiệu quả nhất hoặc ngắn nhất, nhưng tôi muốn có khoảng trống để tách câu trả lời khỏi câu hỏi. Mục đích của câu trả lời của tôi là để cảnh báo chống lại các chuỗi ma thuật hơn là cung cấp một câu trả lời sáng tạo lạ mắt.


Tại sao tôi quan tâm rằng không có " I?"
Christine

1
Chữ và số (bỏ qua trường hợp) là [A-Z0-9]. Nếu, tình cờ, chuỗi ngẫu nhiên của bạn chỉ bao gồm [A-HJ-Z0-9]kết quả không bao gồm toàn bộ phạm vi cho phép, điều này có thể có vấn đề.
Lee

Làm thế nào mà có vấn đề? Vì vậy, nó không chứa I. Có phải vì có một ký tự ít hơn và điều đó làm cho nó dễ bị bẻ khóa hơn? Số liệu thống kê về mật khẩu có thể bẻ khóa có chứa 35 ký tự trong phạm vi hơn 36. Tôi nghĩ rằng tôi muốn mạo hiểm hơn ... hoặc chỉ bằng chứng chứng minh phạm vi ký tự ... hơn là bao gồm tất cả rác đó trong mã của tôi. Nhưng, đó là tôi. Ý tôi là, không phải là một lỗ mông, tôi chỉ nói. Đôi khi tôi nghĩ các lập trình viên có xu hướng đi theo con đường cực kỳ phức tạp vì lợi ích của việc quá phức tạp.
Christine

1
Nó chuyên sâu về trường hợp sử dụng. Rất phổ biến để loại trừ các ký tự như IOtừ các loại chuỗi ngẫu nhiên này để tránh con người nhầm lẫn chúng với 10. Nếu bạn không quan tâm đến việc có một chuỗi có thể đọc được của con người, tốt thôi, nhưng nếu đó là thứ gì đó mà ai đó có thể cần phải gõ, thì thực sự thông minh để loại bỏ các ký tự đó.
Chris Pratt

5

Một phiên bản sạch hơn của giải pháp 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);

Sở thích phong cách của bạn có thể thay đổi.


Điều này tốt hơn nhiều và hiệu quả hơn câu trả lời được chấp nhận.
Nêm

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

5

Sau khi xem xét các câu trả lời khác và xem xét nhận xét của CodeInChaos, cùng với CodeInChaos vẫn trả lời sai (mặc dù ít hơn), tôi nghĩ rằng cần có giải pháp cắt và dán cuối cùng . Vì vậy, trong khi cập nhật câu trả lời của tôi, tôi quyết định đi ra ngoài.

Để có phiên bản cập nhật của mã này, vui lòng truy cập kho lưu trữ Hg mới trên Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Tôi khuyên bạn nên sao chép và dán mã từ: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (chắc chắn rằng bạn nhấp nút Raw để sao chép dễ dàng hơn và đảm bảo bạn có phiên bản mới nhất, tôi nghĩ rằng liên kết này đi đến một phiên bản cụ thể của mã chứ không phải mới nhất).

Ghi chú cập nhật:

  1. Liên quan đến một số câu trả lời khác - Nếu bạn biết độ dài của đầu ra, bạn không cần StringBuilder và khi sử dụng ToCharArray, điều này sẽ tạo và lấp đầy mảng (trước tiên bạn không cần tạo một mảng trống)
  2. Liên quan đến một số câu trả lời khác - Bạn nên sử dụng NextBytes, thay vì nhận từng câu một để thực hiện
  3. Về mặt kỹ thuật, bạn có thể ghim mảng byte để truy cập nhanh hơn .. nó thường có giá trị khi lặp lại hơn 6-8 lần trên một mảng byte. (Không được thực hiện ở đây)
  4. Sử dụng RNGCryptoServiceProvider để có sự ngẫu nhiên tốt nhất
  5. Việc sử dụng bộ đệm của bộ đệm dữ liệu ngẫu nhiên 1MB - điểm chuẩn cho thấy tốc độ truy cập byte đơn được lưu trong bộ nhớ cache nhanh hơn ~ 1000 lần - mất 9ms trên 1MB so với 989ms khi không được lưu trữ.
  6. Tối ưu hóa từ chối vùng thiên vị trong lớp mới của tôi.

Kết thúc giải pháp cho câu hỏi:

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

Nhưng bạn cần lớp mới (chưa được kiểm tra) của tôi:

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

Đối với lịch sử - giải pháp cũ hơn của tôi cho câu trả lời này, đã sử dụng đối tượng Ngẫu nhiên:

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

Hiệu suất:

  1. SecureFastRandom - Lần chạy đầu tiên = ~ 9-33ms . Vô thường. Đang thực hiện : 5ms (đôi khi lên tới 13ms) trên 10.000 lần lặp, với số lần lặp trung bình duy nhất = 1,5 micro giây. . Lưu ý: Yêu cầu thường là 2, nhưng đôi khi có tới 8 lần làm mới bộ đệm - phụ thuộc vào số lượng byte đơn vượt quá vùng thiên vị
  2. Ngẫu nhiên - Lần chạy đầu tiên = ~ 0-1ms . Vô thường. Đang thực hiện : 5ms trên 10.000 lần lặp. Với một lần lặp trung bình duy nhất = 0,55 giây. . Về tốc độ như nhau.

Ngoài ra kiểm tra:

Những liên kết này là một cách tiếp cận khác. Bộ đệm có thể được thêm vào cơ sở mã mới này, nhưng quan trọng nhất là khám phá các cách tiếp cận khác nhau để loại bỏ sự thiên vị, và điểm chuẩn tốc độ và ưu / nhược điểm.


Tôi đã tìm thấy một số cải tiến hiệu suất nhỏ cho phương pháp của bạn, có vẻ như đã nhịn ăn trong nhóm - stackoverflow.com/a/17092645/1037948
drzaus

5
1) Tại sao tất cả các hằng số ma thuật? Bạn đã chỉ định độ dài đầu ra ba lần. Chỉ cần định nghĩa nó là một hằng số hoặc một tham số. Bạn có thể sử dụng charSet.Lengththay vì 62. 2) Một tĩnh Randommà không khóa có nghĩa là mã này không phải là chủ đề an toàn. 3) giảm 0-255 mod 62 giới thiệu một thiên vị có thể phát hiện được. 4) Bạn không thể sử dụng ToStringtrên một mảng char, luôn trả về "System.Char[]". Bạn cần sử dụng new String(rName)thay thế.
CodeInChaos

Cảm ơn @CodesInChaos, tôi chưa bao giờ nghĩ về những điều đó trước đó. Vẫn chỉ sử dụng lớp Random, nhưng điều này sẽ tốt hơn. Tôi không thể nghĩ ra cách nào tốt hơn để phát hiện và sửa lỗi đầu vào sai lệch.
Todd

Thật là hơi ngớ ngẩn khi bắt đầu với RNG ( System.Random) yếu và sau đó cẩn thận tránh mọi sai lệch trong mã của riêng bạn. Thành ngữ "đánh bóng một con rùa" xuất hiện trong tâm trí.
CodeInChaos

@CodesInChaos Và bây giờ người học việc đã vượt qua chủ nhân của mình
Todd

4

Thật kinh khủng, tôi biết, nhưng tôi không thể tự giúp mình:


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


4

Tôi đang tìm kiếm một câu trả lời cụ thể hơn, nơi tôi muốn kiểm soát định dạng của chuỗi ngẫu nhiên và tình cờ thấy bài đăng này. Ví dụ: biển số xe (của ô tô) có định dạng cụ thể (theo quốc gia) và tôi muốn tạo biển số ngẫu nhiên.
Tôi quyết định viết phương pháp mở rộng ngẫu nhiên của riêng tôi cho việc này. (điều này là để sử dụng lại cùng một đối tượng Ngẫu nhiên, vì bạn có thể có gấp đôi trong các tình huống đa luồng). Tôi đã tạo một ý chính ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), nhưng cũng sẽ sao chép lớp mở rộng tại đây:

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

4

Bây giờ trong một hương vị lót.

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

2
Sử dụng một tài sản cho một cái gì đó thay đổi trên mỗi truy cập là khá đáng ngờ. Tôi khuyên bạn nên sử dụng một phương pháp thay thế.
CodeInChaos

2
RNGCryptoServiceProvidernên được xử lý sau khi sử dụng.
Tsahi Asher

Tôi đã khắc phục sự cố IDis Dùng một lần, nhưng điều này vẫn rất đáng ngờ, tạo RNGCryptoServiceProvider mới cho mỗi chữ cái.
piojo

@CodesInChaos đã xong, giờ là một phương thức.
Matas Vaitkevicius

3

Cố gắng kết hợp hai phần: duy nhất (chuỗi, bộ đếm hoặc ngày) và ngẫu nhiên

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

Các xét nghiệm:

[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())
    }

1) Bạn có thể sử dụng các ký tự bằng chữ thay vì các giá trị ASCII được liên kết với các ký tự đó. 2) Bạn có một lỗi sai trong mã khớp khoảng thời gian của bạn. Bạn cần sử dụng <=>=thay vì <>. 3) Tôi sẽ thêm các dấu ngoặc đơn không cần thiết xung quanh các &&biểu thức để làm rõ rằng chúng có quyền ưu tiên, nhưng tất nhiên đó chỉ là một lựa chọn phong cách.
CodeInChaos

+ 1 Tốt để loại bỏ sự thiên vị và thêm thử nghiệm. Tôi không chắc tại sao bạn lại thêm chuỗi ngẫu nhiên của mình bằng chuỗi có dấu thời gian? Ngoài ra, bạn vẫn cần phải xử lý RNGCryptoServiceProvider của mình
monty

2

Một giải pháp mà không cần sử dụng Random:

var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());

2
NewGuid sử dụng ngẫu nhiên trong nội bộ. Vì vậy, điều này vẫn đang sử dụng ngẫu nhiên, nó chỉ che giấu nó.
Nêm

2

Đây là một biến thể của giải pháp của Eric J, tức là âm thanh mật mã, cho WinRT (Ứng dụng 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();
}

Nếu hiệu suất quan trọng (đặc biệt là khi độ dài cao):

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

1
Đây không phải là âm thanh mật mã. Có một sai lệch nhỏ do hoạt động mô đun không trải toàn bộ chiều rộng của ulong bằng nhau thành 62 ký tự.
Lie Ryan

1

Tôi biết điều này không phải là cách tốt nhất. Nhưng bạn có thể thử điều này.

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);

2
Làm thế nào là một dòng? Console.WriteLine ($ "Chuỗi ngẫu nhiên: {Path.GetRandomFileName (). Thay thế (". "," ")}"); là một dòng.
PmanAce

1

Tôi không biết âm thanh này nghe như thế nào, nhưng nó dễ đọc và ngắn gọn hơn các giải pháp phức tạp hơn (imo) và nó sẽ là " System.Randomgiải pháp ngẫu nhiên" hơn so với các giải pháp dựa trên.

return alphabet
    .OrderBy(c => Guid.NewGuid())
    .Take(strLength)
    .Aggregate(
        new StringBuilder(),
        (builder, c) => builder.Append(c))
    .ToString();

Tôi không thể quyết định liệu tôi nghĩ phiên bản này hay phiên bản tiếp theo "đẹp hơn", nhưng chúng cho kết quả chính xác như nhau:

return new string(alphabet
    .OrderBy(o => Guid.NewGuid())
    .Take(strLength)
    .ToArray());

Cấp, nó không được tối ưu hóa cho tốc độ, vì vậy nếu nhiệm vụ quan trọng là tạo ra hàng triệu chuỗi ngẫu nhiên mỗi giây, hãy thử một chuỗi khác!

LƯU Ý: Giải pháp này không cho phép lặp lại các ký hiệu trong bảng chữ cái và bảng chữ cái PHẢI có kích thước bằng hoặc lớn hơn chuỗi đầu ra, làm cho cách tiếp cận này ít được mong muốn hơn trong một số trường hợp, tất cả phụ thuộc vào trường hợp sử dụng của bạn.


0

Nếu các giá trị của bạn không hoàn toàn ngẫu nhiên, nhưng trên thực tế có thể phụ thuộc vào một cái gì đó - bạn có thể tính toán băm md5 hoặc sha1 của 'thứ gì đó' và sau đó cắt nó theo bất kỳ độ dài nào bạn muốn.

Ngoài ra, bạn có thể tạo và cắt bớt một hướng dẫn.


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

2
1) phương pháp tĩnh nên là chủ đề an toàn. 2) Điểm tăng chỉ số thay vì sử dụng kết quả random.Nexttrực tiếp là gì? Làm phức tạp mã và không đạt được bất cứ điều gì hữu ích.
CodeInChaos

0

Đây là một cơ chế để tạo ra một chuỗi số alpha ngẫu nhiên (tôi sử dụng điều này để tạo mật khẩu và dữ liệu thử nghiệm) mà không cần xác định bảng chữ cái và số,

CleanupBase64 sẽ loại bỏ các phần cần thiết trong chuỗi và tiếp tục thêm các chữ cái alpha-số ngẫu nhiên theo cách đệ quy.

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

Bạn đã khai báo GenerateRandomStringvà thực hiện cuộc gọi GetRandomStringtừ bên trong SanitiseBase64String. Ngoài ra, bạn đã khai báo SanitiseBase64String và gọi CleanUpBase64Stringtrong GenerateRandomString.
Wai Ha Lee

0

Có một trong những gói nuget tuyệt vời làm cho việc này trở nên đơn giản.

var myObject = new Faker<MyObject>()
.RuleFor(p => p.MyAlphaNumericProperty, f => f.Random.AlphaNumeric(/*lenght*/ 7))
.Generate();

Một trong những ví dụ điển hình là đây .


0

không chắc chắn 100%, vì tôi đã không kiểm tra MỌI tùy chọn ở đây, nhưng trong số những cái tôi đã kiểm tra, cái này là nhanh nhất. đã hẹn giờ với đồng hồ bấm giờ và nó hiển thị 9-10 tích, vì vậy nếu tốc độ quan trọng hơn bảo mật, hãy thử điều này:

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

Tại sao trả lời? Câu hỏi này đã được trả lời rất nhiều lần và bạn vẫn đăng câu trả lời nửa vời của mình ..
Laurent
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.