Làm cách nào để tạo chuỗi ký tự gồm 8 ký tự ngẫu nhiên trong C #?
Random
lớp để tạo mật khẩu. Việc gieo hạt Random
có 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.
Làm cách nào để tạo chuỗi ký tự gồm 8 ký tự ngẫu nhiên trong C #?
Random
lớp để tạo mật khẩu. Việc gieo hạt Random
có 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.
Câu trả lời:
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 Random
lớ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 RNGCryptoServiceProvider
lớp nếu bạn cần một trình tạo số ngẫu nhiên mạnh.)
return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
var finalString = new String(stringChars);
Không thanh lịch như giải pháp Linq.
(Lưu ý: Việc sử dụng Random
lớ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 RNGCryptoServiceProvider
lớp nếu bạn cần một trình tạo số ngẫu nhiên mạnh.)
GetRandomFileName
Giả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ự. Guid
Giả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ự.
GetRandomFileName
như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.
System.Random
không phù hợp với an ninh.
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")}%");
}
}
}
}
RNGCSP
ở nơi đầu tiên?) Sử dụng mod để chỉ mục vào chars
phương tiện mảng mà bạn sẽ nhận được thiên vị đầu ra trừ khi chars.Length
xảy ra là một ước của 256.
4*maxSize
byte ngẫu nhiên, sau đó sử dụng (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length
. Tôi cũng sẽ sử dụng GetBytes
thay 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ó.
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.
Đâ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
Các mục tiêu chính của mã của tôi là:
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 RNGCryptoServiceProvider
thay 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);
}
Đ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.GetRandomFileName
cá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
.
chars.Select
là 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.
'a'.To('z')
cách tiếp cận?
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.Range
hoặ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
.
chars.Count
là 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 Take
không trực quan phải không? Tôi không tin điều đó Cảm ơn đã chỉ lỗi đánh máy.
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:
// 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);
}
Đã 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 NonOptimized
thực sự nhanh hơn, và đôi khi ForLoop
và GenerateRandomString
chuyể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]
var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());
, nơi bạn thay thế EachRandomizingMethod
bằng ... mỗi phương thức
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.
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), bcdefgh
có thể xảy ra hơn một chút so với các mật khẩu khác ( a
không phải bởi vì GetNonZeroBytes
nó không tạo ra các byte có giá trị bằng 0, vì vậy độ lệch đối với a
nó đượ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);
}
}
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);
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);
}
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());
Câu hỏi: Tại sao tôi nên lãng phí thời gian sử dụng Enumerable.Range
thay 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.Random
nế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.
I
?"
[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 đề.
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.
I
và O
từ 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 1
và 0
. 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ự đó.
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.
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:
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:
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.
charSet.Length
thay vì 62
. 2) Một tĩnh Random
mà 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 ToString
trên một mảng char, luôn trả về "System.Char[]"
. Bạn cần sử dụng new String(rName)
thay thế.
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í.
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);
}
}
}
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)];
}
}
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());
}
RNGCryptoServiceProvider
nên được xử lý sau khi sử dụng.
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())
}
<=
và >=
thay vì <
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.
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());
Đâ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();
}
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);
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.Random
giả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.
public static class StringHelper
{
private static readonly Random random = new Random();
private const int randomSymbolsDefaultCount = 8;
private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static int randomSymbolsIndex = 0;
public static string GetRandomSymbols()
{
return GetRandomSymbols(randomSymbolsDefaultCount);
}
public static string GetRandomSymbols(int count)
{
var index = randomSymbolsIndex;
var result = new string(
Enumerable.Repeat(availableChars, count)
.Select(s => {
index += random.Next(s.Length);
if (index >= s.Length)
index -= s.Length;
return s[index];
})
.ToArray());
randomSymbolsIndex = index;
return result;
}
}
random.Next
trự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.
Đâ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);
}
GenerateRandomString
và thực hiện cuộc gọi GetRandomString
từ bên trong SanitiseBase64String
. Ngoài ra, bạn đã khai báo SanitiseBase64String
và gọi CleanUpBase64String
trong GenerateRandomString
.
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);
}
}