C #에서 임의의 8 자 영숫자 문자열을 생성하려면 어떻게해야합니까?
답변
LINQ가 새로운 블랙이라고 들었습니다. LINQ를 사용한 시도는 다음과 같습니다.
private static Random random = new Random();
public static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
(참고 : Random
클래스를 사용하면 암호 또는 토큰 생성과 같은 보안 관련 항목에 적합하지 않습니다 . RNGCryptoServiceProvider
강력한 난수 생성기가 필요한 경우 클래스를 사용하십시오 .)
답변
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
var finalString = new String(stringChars);
Linq 솔루션만큼 우아하지는 않습니다.
(참고 : Random
클래스를 사용하면 암호 또는 토큰 생성과 같은 보안 관련 항목에 적합하지 않습니다 . RNGCryptoServiceProvider
강력한 난수 생성기가 필요한 경우 클래스를 사용하십시오 .)
답변
주석을 기반으로 업데이트되었습니다. 원래 구현은 ~ 1.95 %의 시간과 나머지 문자 ~ 1.56 %의 시간을 생성했습니다. 업데이트는 ~ 1.61 %의 모든 문자를 생성합니다.
프레임 워크 지원 -.NET Core 3 (및 .NET Standard 2.1 이상을 지원하는 향후 플랫폼)은 원하는 범위 내에서 임의의 정수를 생성하기 위해 암호 적으로 안전한 방법 인 RandomNumberGenerator.GetInt32 () 를 제공합니다 .
제시된 대안 중 일부와 달리이 방법은 암호 적으로 안전 합니다.
using System;
using System.Security.Cryptography;
using System.Text;
namespace UniqueKey
{
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4*size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
public static string GetUniqueKeyOriginal_BIASED(int size)
{
char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
foreach (byte b in data)
{
result.Append(chars[b % (chars.Length)]);
}
return result.ToString();
}
}
}
대안의 논의를 바탕으로 여기에 아래 의견에 따라 업데이트 / 수정 .
다음은 이전 및 업데이트 된 출력의 문자 분포를 보여주는 작은 테스트 장치입니다. randomness 분석에 대한 자세한 내용은 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")}%");
}
}
}
}
답변
해결 방법 1-가장 유연한 길이의 가장 큰 ‘범위’
string get_unique_string(int string_length) {
using(var rng = new RNGCryptoServiceProvider()) {
var bit_count = (string_length * 6);
var byte_count = ((bit_count + 7) / 8); // rounded up
var bytes = new byte[byte_count];
rng.GetBytes(bytes);
return Convert.ToBase64String(bytes);
}
}
이 솔루션은 GUID를 사용하는 것보다 범위가 넓습니다. GUID에는 항상 동일하고 임의적이지 않은 고정 된 비트가 두 개 있기 때문에 16 진수로 된 13자는 항상 “4”입니다. 최소한 버전 6 GUID에서는.
이 솔루션을 사용하면 모든 길이의 문자열을 생성 할 수 있습니다.
해결 방법 2-한 줄의 코드-최대 22 자에 적합
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);
솔루션 1 만큼 문자열을 생성 할 수 없으며 GUID의 고정 비트로 인해 문자열의 범위가 동일하지 않지만 대부분의 경우이 작업을 수행합니다.
해결 방법 3-약간 적은 코드
Guid.NewGuid().ToString("n").Substring(0, 8);
대부분 역사적 목적으로 여기에 보관하십시오. 약간 적은 코드를 사용하지만 범위를 줄이는 데 드는 비용이 들지만 base64 대신 16 진수를 사용하기 때문에 다른 솔루션과 동일한 범위를 나타내는 데 더 많은 문자가 필요합니다.
이는 더 많은 충돌 가능성을 의미합니다. 8 개의 문자열을 100,000 번 반복하여 테스트하면 하나의 사본이 생성됩니다.
답변
다음은 Dot Net Perls의 Sam Allen 예제에서 얻은 예입니다.
8 자만 필요한 경우 System.IO 네임 스페이스에서 Path.GetRandomFileName ()을 사용하십시오. “Path.GetRandomFileName 메서드를 사용하면 RNGCryptoServiceProvider를 사용하여 임의성을 향상시킬 수 있기 때문에 때때로 우수합니다. 그러나 11 개의 임의 문자로 제한됩니다.”
GetRandomFileName은 항상 9 번째 문자에 마침표가있는 12 자 문자열을 반환합니다. 따라서 임의의 것이 아니므로 마침표를 제거한 다음 문자열에서 8자를 가져와야합니다. 실제로 처음 8자를 가져 와서 마침표에 대해 걱정하지 않아도됩니다.
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
추신 : 감사합니다 샘
답변
내 코드의 주요 목표는 다음과 같습니다.
- 줄의 분포는 거의 균일합니다 (작은 한 작은 편차는 신경 쓰지 마십시오)
- 각 인수 세트에 대해 수십억 개 이상의 문자열을 출력합니다. PRNG가 20 억 (31 비트의 엔트로피) 만 다른 값을 생성하는 경우 8 문자열 (~ 47 비트의 엔트로피)을 생성하는 것은 의미가 없습니다.
- 사람들이 암호 또는 다른 보안 토큰에 이것을 사용할 것으로 기대하기 때문에 안전합니다.
첫 번째 속성은 알파벳 크기의 64 비트 값 모듈로를 취함으로써 달성됩니다. 작은 알파벳 (예 : 질문의 62 자)의 경우 이는 무시할만한 편향으로 이어집니다. 두 번째 및 세 번째 속성은 RNGCryptoServiceProvider
대신을 사용하여 수행됩니다 System.Random
.
using System;
using System.Security.Cryptography;
public static string GetRandomAlphanumericString(int length)
{
const string alphanumericCharacters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz" +
"0123456789";
return GetRandomString(length, alphanumericCharacters);
}
public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
if (length < 0)
throw new ArgumentException("length must not be negative", "length");
if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
throw new ArgumentException("length is too big", "length");
if (characterSet == null)
throw new ArgumentNullException("characterSet");
var characterArray = characterSet.Distinct().ToArray();
if (characterArray.Length == 0)
throw new ArgumentException("characterSet must not be empty", "characterSet");
var bytes = new byte[length * 8];
var result = new char[length];
using (var cryptoProvider = new RNGCryptoServiceProvider())
{
cryptoProvider.GetBytes(bytes);
}
for (int i = 0; i < length; i++)
{
ulong value = BitConverter.ToUInt64(bytes, i * 8);
result[i] = characterArray[value % (uint)characterArray.Length];
}
return new string(result);
}
답변
가장 간단한 :
public static string GetRandomAlphaNumeric()
{
return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}
char 배열을 하드 코딩하고 다음을 사용하면 더 나은 성능을 얻을 수 있습니다 System.Random
.
public static string GetRandomAlphaNumeric()
{
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
영어 알파벳이 언젠가 변경되어 비즈니스를 잃을 수 있다고 걱정하는 경우 하드 코딩을 피할 수는 있지만 약간 더 나빠질 수 있습니다 ( Path.GetRandomFileName
접근하는 데 비해 )
public static string GetRandomAlphaNumeric()
{
var chars = 'a'.To('z').Concat('0'.To('9')).ToList();
return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
public static IEnumerable<char> To(this char start, char end)
{
if (end < start)
throw new ArgumentOutOfRangeException("the end char should not be less than start char", innerException: null);
return Enumerable.Range(start, end - start + 1).Select(i => (char)i);
}
마지막 두 가지 접근 방식을 확장 방법으로 만들면 더 좋아 보입니다. System.Random
인스턴스 .