C# 独特的随机字符串生成
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/730268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unique random string generation
提问by Kirtan
I'd like to generate random unique strings like the ones being generated by MSDN library.(Error Object), for example. A string like 't9zk6eay' should be generated.
例如,我想生成像 MSDN 库生成的那样的随机唯一字符串。(错误对象)。应该生成像 't9zk6eay' 这样的字符串。
采纳答案by Mark Synowiec
Using Guid would be a pretty good way, but to get something looking like your example, you probably want to convert it to a Base64 string:
使用 Guid 将是一个很好的方法,但要获得类似于您的示例的内容,您可能希望将其转换为 Base64 字符串:
Guid g = Guid.NewGuid();
string GuidString = Convert.ToBase64String(g.ToByteArray());
GuidString = GuidString.Replace("=","");
GuidString = GuidString.Replace("+","");
I get rid of "=" and "+" to get a little closer to your example, otherwise you get "==" at the end of your string and a "+" in the middle. Here's an example output string:
我摆脱了“=”和“+”以更接近您的示例,否则您会在字符串末尾得到“==”,在中间得到“+”。这是一个示例输出字符串:
"OZVV5TpP4U6wJthaCORZEQ"
“OZVV5TpP4U6wJthaCORZEQ”
回答by Fabian Vilers
- not sure Microsoft's link are randomly generated
- have a look to new Guid().ToString()
- 不确定微软的链接是随机生成的
- 看看 new Guid().ToString()
回答by Lucero
I don't think that they really are random, but my guess is those are some hashes.
我不认为它们真的是随机的,但我的猜测是这些是一些散列。
Whenever I need some random identifier, I usually use a GUID and convert it to its "naked" representation:
每当我需要一些随机标识符时,我通常使用一个 GUID 并将其转换为它的“裸”表示:
Guid.NewGuid().ToString("n");
回答by Chris Doggett
Get Unique Key using GUID Hash code
public static string GetUniqueKey(int length)
{
string guidResult = string.Empty;
while (guidResult.Length < length)
{
// Get the GUID.
guidResult += Guid.NewGuid().ToString().GetHashCode().ToString("x");
}
// Make sure length is valid.
if (length <= 0 || length > guidResult.Length)
throw new ArgumentException("Length must be between 1 and " + guidResult.Length);
// Return the first length bytes.
return guidResult.Substring(0, length);
}
回答by Pontus Gagge
This has been asked for various languages. Here's one question about passwordswhich should be applicable here as well.
这已被要求用于各种语言。这是一个关于密码的问题,它也应该适用于此。
If you want to use the strings for URL shortening, you'll also need a Dictionary<> or database check to see whether a generated ID has already been used.
如果您想使用字符串来缩短 URL,您还需要一个 Dictionary<> 或数据库检查以查看是否已使用生成的 ID。
回答by Keltex
I would caution that GUIDs are not random numbers. They should not be used as the basis to generate anything that you expect to be totally random (see http://en.wikipedia.org/wiki/Globally_Unique_Identifier):
我要提醒的是 GUID不是随机数。它们不应用作生成任何您希望完全随机的内容的基础(请参阅http://en.wikipedia.org/wiki/Globally_Unique_Identifier):
Cryptanalysis of the WinAPI GUID generator shows that, since the sequence of V4 GUIDs is pseudo-random, given the initial state one can predict up to next 250 000 GUIDs returned by the function UuidCreate. This is why GUIDs should not be used in cryptography, e. g., as random keys.
WinAPI GUID 生成器的密码分析表明,由于 V4 GUID 的序列是伪随机的,在给定初始状态的情况下,可以预测由函数 UuidCreate 返回的下一个 250 000 个 GUID。这就是为什么 GUID 不应在密码学中使用的原因,例如,作为随机密钥。
Instead, just use the C# Random method. Something like this (code found here):
相反,只需使用 C# Random 方法。像这样的东西(代码在这里找到):
private string RandomString(int size)
{
StringBuilder builder = new StringBuilder();
Random random = new Random();
char ch ;
for(int i=0; i<size; i++)
{
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))) ;
builder.Append(ch);
}
return builder.ToString();
}
GUIDs are fine if you want something unique(like a unique filename or key in a database), but they are not good for something you want to be random(like a password or encryption key). So it depends on your application.
如果您想要唯一的东西(例如数据库中唯一的文件名或密钥),GUID 很好,但对于您想要随机的东西(例如密码或加密密钥),它们不适合。所以这取决于你的应用程序。
Edit. Microsoft says that Random is not that great either (http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx):
编辑。微软表示 Random 也不是很好(http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx):
To generate a cryptographically secure random number suitable for creating a random password, for example, use a class derived from System.Security.Cryptography.RandomNumberGenerator such as System.Security.Cryptography.RNGCryptoServiceProvider.
例如,要生成适合创建随机密码的加密安全随机数,请使用派生自 System.Security.Cryptography.RandomNumberGenerator 的类,例如 System.Security.Cryptography.RNGCryptoServiceProvider。
回答by Michael Kropat
Update 2016/1/23
更新 2016/1/23
If you find this answer useful, you may be interested in a simple (~500 SLOC) password generation library I published:
如果你觉得这个答案很有用,你可能会对我发布的一个简单的(~500 SLOC)密码生成库感兴趣:
Install-Package MlkPwgen
Then you can generate random strings just like in the answer below:
然后你可以像下面的答案一样生成随机字符串:
var str = PasswordGenerator.Generate(length: 10, allowed: Sets.Alphanumerics);
One advantage of the library is that the code is better factored out so you can use secure randomness for more than generating strings. Check out the project sitefor more details.
该库的一个优点是代码可以更好地分解,因此您可以将安全随机性用于生成字符串之外的更多内容。查看项目站点以获取更多详细信息。
Original Answer
原答案
Since no one has provided secure code yet, I post the following in case anyone finds it useful.
由于还没有人提供安全代码,我发布以下内容,以防有人发现它有用。
string RandomString(int length, string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
if (length < 0) throw new ArgumentOutOfRangeException("length", "length cannot be less than zero.");
if (string.IsNullOrEmpty(allowedChars)) throw new ArgumentException("allowedChars may not be empty.");
const int byteSize = 0x100;
var allowedCharSet = new HashSet<char>(allowedChars).ToArray();
if (byteSize < allowedCharSet.Length) throw new ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize));
// Guid.NewGuid and System.Random are not particularly random. By using a
// cryptographically-secure random number generator, the caller is always
// protected, regardless of use.
using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create()) {
var result = new StringBuilder();
var buf = new byte[128];
while (result.Length < length) {
rng.GetBytes(buf);
for (var i = 0; i < buf.Length && result.Length < length; ++i) {
// Divide the byte into allowedCharSet-sized groups. If the
// random value falls into the last group and the last group is
// too small to choose from the entire allowedCharSet, ignore
// the value in order to avoid biasing the result.
var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);
if (outOfRangeStart <= buf[i]) continue;
result.Append(allowedCharSet[buf[i] % allowedCharSet.Length]);
}
}
return result.ToString();
}
}
Thanks to Ahmad for pointing out how to get the code working on .NET Core.
感谢 Ahmad 指出如何让代码在 .NET Core 上运行。
回答by Oskar Sj?berg
I simplified @Michael Kropats solution and made a LINQ-esque version.
我简化了@Michael Kropats 解决方案并制作了一个 LINQ 风格的版本。
string RandomString(int length, string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
{
var outOfRange = byte.MaxValue + 1 - (byte.MaxValue + 1) % alphabet.Length;
return string.Concat(
Enumerable
.Repeat(0, int.MaxValue)
.Select(e => RandomByte())
.Where(randomByte => randomByte < outOfRange)
.Take(length)
.Select(randomByte => alphabet[randomByte % alphabet.Length])
);
}
byte RandomByte()
{
using (var randomizationProvider = new RNGCryptoServiceProvider())
{
var randomBytes = new byte[1];
randomizationProvider.GetBytes(randomBytes);
return randomBytes.Single();
}
}
回答by jhersey29
Michael Kropats solution in VB.net
VB.net 中的 Michael Kropats 解决方案
Private Function RandomString(ByVal length As Integer, Optional ByVal allowedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") As String
If length < 0 Then Throw New ArgumentOutOfRangeException("length", "length cannot be less than zero.")
If String.IsNullOrEmpty(allowedChars) Then Throw New ArgumentException("allowedChars may not be empty.")
Dim byteSize As Integer = 256
Dim hash As HashSet(Of Char) = New HashSet(Of Char)(allowedChars)
'Dim hash As HashSet(Of String) = New HashSet(Of String)(allowedChars)
Dim allowedCharSet() = hash.ToArray
If byteSize < allowedCharSet.Length Then Throw New ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize))
' Guid.NewGuid and System.Random are not particularly random. By using a
' cryptographically-secure random number generator, the caller is always
' protected, regardless of use.
Dim rng = New System.Security.Cryptography.RNGCryptoServiceProvider()
Dim result = New System.Text.StringBuilder()
Dim buf = New Byte(128) {}
While result.Length < length
rng.GetBytes(buf)
Dim i
For i = 0 To buf.Length - 1 Step +1
If result.Length >= length Then Exit For
' Divide the byte into allowedCharSet-sized groups. If the
' random value falls into the last group and the last group is
' too small to choose from the entire allowedCharSet, ignore
' the value in order to avoid biasing the result.
Dim outOfRangeStart = byteSize - (byteSize Mod allowedCharSet.Length)
If outOfRangeStart <= buf(i) Then
Continue For
End If
result.Append(allowedCharSet(buf(i) Mod allowedCharSet.Length))
Next
End While
Return result.ToString()
End Function
回答by Timo
If you want an alphanumeric strings with lowercase anduppercase characters ([a-zA-Z0-9]), you can use Convert.ToBase64String() for a fast and simple solution.
如果您想要带有小写和大写字符 ([a-zA-Z0-9])的字母数字字符串,您可以使用 Convert.ToBase64String() 来获得快速而简单的解决方案。
As for uniqueness, check out the birthday problemto calculate how likely a collission is given (A) the length of the strings generated and (B) the number of strings generated.
至于唯一性,请查看生日问题以计算在给定 (A) 生成的字符串的长度和 (B) 生成的字符串数量的情况下发生冲突的可能性。
Random random = new Random();
int outputLength = 10;
int byteLength = (int)Math.Ceiling(3f / 4f * outputLength); // Base64 uses 4 characters for every 3 bytes of data; so in random bytes we need only 3/4 of the desired length
byte[] randomBytes = new byte[byteLength];
string output;
do
{
random.NextBytes(randomBytes); // Fill bytes with random data
output = Convert.ToBase64String(randomBytes); // Convert to base64
output = output.Substring(0, outputLength); // Truncate any superfluous characters and/or padding
} while (output.Contains('/') || output.Contains('+')); // Repeat if we contain non-alphanumeric characters (~25% chance if length=10; ~50% chance if length=20; ~35% chance if length=32)