我如何在c#中生成一个随机的8个字符的字母数字字符串?
当前回答
我不知道这在密码学上听起来如何,但它比迄今为止(在我看来)更复杂的解决方案更具可读性和简练性,而且它应该比系统更“随机”。Random-based解决方案。
return alphabet
.OrderBy(c => Guid.NewGuid())
.Take(strLength)
.Aggregate(
new StringBuilder(),
(builder, c) => builder.Append(c))
.ToString();
我不知道我认为这个版本还是下一个版本“更漂亮”,但它们给出了完全相同的结果:
return new string(alphabet
.OrderBy(o => Guid.NewGuid())
.Take(strLength)
.ToArray());
当然,它并没有针对速度进行优化,所以如果每秒生成数百万个随机字符串是关键任务,请尝试另一个!
注意:此解决方案不允许字母中符号的重复,并且字母必须等于或大于输出字符串的大小,使得这种方法在某些情况下不太可取,这完全取决于您的用例。
其他回答
不使用Random的解决方案:
var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);
var randomStr = new string(chars.SelectMany(str => str)
.OrderBy(c => Guid.NewGuid())
.Take(8).ToArray());
这是我从Dot Net Perls的Sam Allen那里偷来的一个例子
如果你只需要8个字符,那么在系统中使用Path.GetRandomFileName()。IO命名空间。Sam说使用“Path.”这里的GetRandomFileName方法有时更优越,因为它使用RNGCryptoServiceProvider来获得更好的随机性。然而,它被限制为11个随机字符。”
GetRandomFileName总是返回一个12个字符的字符串,第9个字符是句点。所以你需要去掉句点(因为这不是随机的),然后从字符串中取出8个字符。实际上,你可以只取前8个字符而不用考虑句点。
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
PS:谢谢,Sam
尝试将两部分结合起来:独特(序列、计数器或日期)和随机
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;
using (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)
{
const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish to make the 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;
}
}
测试:
[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())
}
在查看了其他答案并考虑了CodeInChaos的评论,以及CodeInChaos仍然有偏见(尽管较少)的答案之后,我认为需要一个最终的终极剪切和粘贴解决方案。所以在更新我的答案时,我决定全力以赴。
For an up to date version of this code, please visit the new Hg repository on Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom. I recommend you copy and paste the code from: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (make sure you click the Raw button to make it easier to copy and make sure you have the latest version, I think this link goes to a specific version of the code, not the latest).
更新说明:
Relating to some other answers - If you know the length of the output, you don't need a StringBuilder, and when using ToCharArray, this creates and fills the array (you don't need to create an empty array first) Relating to some other answers - You should use NextBytes, rather than getting one at a time for performance Technically you could pin the byte array for faster access.. it's usually worth it when your iterating more than 6-8 times over a byte array. (Not done here) Use of RNGCryptoServiceProvider for best randomness Use of caching of a 1MB buffer of random data - benchmarking shows cached single bytes access speed is ~1000x faster - taking 9ms over 1MB vs 989ms for uncached. Optimised rejection of bias zone within my new class.
问题的最终解决方案:
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);
}
但是你需要我的新(未经测试的)类:
/// <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
}
}
}
对于历史-我对这个答案的旧解决方案,使用随机对象:
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);
}
性能:
securefastrrandom -第一次运行= ~9-33ms。听不清。持续:5毫秒(有时高达13毫秒)超过10,000次迭代,单次平均迭代= 1.5微秒。注意:通常需要2个缓存刷新,但偶尔需要8个缓存刷新——这取决于有多少单个字节超出了偏置区域 随机-第一次运行= ~0-1ms。听不清。正在进行:5毫秒超过10,000次迭代。单次平均迭代= 0.5微秒。速度差不多。
还可以看看:
https://bitbucket.org/merarischroeder/number-range-with-no-bias/src https://stackoverflow.com/a/45118325/887092
这些联系是另一种方法。缓冲可以添加到这个新的代码库中,但最重要的是探索不同的方法来消除偏差,并对速度和利弊进行基准测试。
我的代码的主要目标是:
弦的分布几乎是均匀的(不关心微小的偏差,只要它们很小) 它为每个参数集输出超过几十亿个字符串。如果您的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);
}
推荐文章
- 实体框架核心:在上一个操作完成之前,在此上下文中开始的第二个操作
- 如何为构造函数定制Visual Studio的私有字段生成快捷方式?
- 为什么Visual Studio 2015/2017/2019测试运行器没有发现我的xUnit v2测试
- 如何使用JSON确保字符串是有效的JSON。网
- AppSettings从.config文件中获取值
- 通过HttpClient向REST API发布一个空体
- 如何检查IEnumerable是否为空或空?
- 如何在Ruby中生成a和b之间的随机数?
- 自动化invokerrequired代码模式
- 没有ListBox。SelectionMode="None",是否有其他方法禁用列表框中的选择?
- 在c#代码中设置WPF文本框的背景颜色
- 在c#中,什么是单子?
- c#和Java中的泛型有什么不同?和模板在c++ ?
- c#线程安全快速(est)计数器
- 如何将此foreach代码转换为Parallel.ForEach?