唯一的随机字符串生成
Posted
技术标签:
【中文标题】唯一的随机字符串生成【英文标题】:Unique random string generation 【发布时间】:2010-10-18 07:59:35 【问题描述】:我想生成随机的唯一字符串,例如由 MSDN 库生成的字符串。(Error Object)。应该生成一个像 't9zk6eay' 这样的字符串。
【问题讨论】:
试试这个string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);
更多可以找到here
对于完全独特的东西,它必须基于非随机的东西,如时间、位置等,因此实际上永远不可能是完全随机的。 Guid 可能看起来是随机的,但实际上并非如此。 IMO 你唯一的希望是让它变得如此随机和复杂,以至于对于所有实际目的,这些值都是唯一的(即碰撞概率极低)。
【参考方案1】:
不确定微软的链接是随机生成的
看看 new Guid().ToString()
【讨论】:
你的意思是 Guid.NewGuid().ToString() - Guid 没有公共构造函数 您可能是对的,输入时没有验证。我相信原始海报有重点。【参考方案2】:我不认为它们真的是随机的,但我猜那些是一些哈希值。
每当我需要一些随机标识符时,我通常会使用 GUID 并将其转换为它的“裸”表示:
Guid.NewGuid().ToString("n");
【讨论】:
正如@Keltex 指出的那样:WinAPI GUID 生成器的密码分析表明,由于 V4 GUID 的序列是伪随机的,因此在初始状态下,可以预测由函数 UuidCreate.【参考方案3】: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);
【讨论】:
这很有效,但随机单词不包含唯一字符。字符是重复的,如 114e3(两个 1)、eaaea(三个 a 和两个 e)、60207(两个 0)等等。如何使用字母数字组合生成不重复字符的随机字符串? @vijay:由于它输出的是十六进制数字,所以您将自己限制为 16 个字符,而 16 个!可能的输出。随机字符串就是这样,随机的。理论上你可以得到一串全 a (aaaaaaaaaaaaaa)。这是非常不可能的,但不比任何其他随机字符串更不可能。我不确定您为什么需要该约束,但是当您向字符串添加字符时,将它们弹出到 HashSet这已被要求提供各种语言。这是one question about passwords,它也应该适用于这里。
如果您想使用字符串来缩短 URL,您还需要字典 或数据库检查以查看生成的 ID 是否已被使用。
【讨论】:
【参考方案5】:我要提醒的是,GUID 不是随机数。它们不应被用作生成任何您期望完全随机的东西的基础(请参阅http://en.wikipedia.org/wiki/Globally_Unique_Identifier):
WinAPI GUID 生成器的密码分析表明,由于 V4 GUID 的序列是伪随机的,因此在初始状态下,可以预测函数 UuidCreate 返回的最多 250 000 个 GUID。这就是为什么不应在密码学中使用 GUID,例如。 g.,作为随机键。
相反,只需使用 C# Random 方法。像这样的东西(code found here):
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();
如果您想要 唯一 的东西(例如数据库中的唯一文件名或键),则 GUID 很好,但它们不适合您想要 随机 的东西(像密码或加密密钥)。所以这取决于你的应用程序。
编辑。微软表示 Random 也不是很好 (http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx):
例如,要生成适合创建随机密码的加密安全随机数,请使用派生自 System.Security.Cryptography.RandomNumberGenerator 的类,例如 System.Security.Cryptography.RNGCryptoServiceProvider。
【讨论】:
C# 随机类也不是“随机的”,也不适合任何加密代码,因为它是一个从特定种子数开始的经典随机生成器。相同的种子也将返回相同的返回数字序列; GUID 方法在这里已经好很多了(不是“随机”而是“唯一”)。 @Lucero:你是对的。 Microsoft 建议,“例如,要生成适合创建随机密码的加密安全随机数,请使用派生自 System.Security.Cryptography.RandomNumberGenerator 的类,例如 System.Security.Cryptography.RNGCryptoServiceProvider。” 好吧,问题已经表明他想要(伪)随机唯一字符串,因此没有加密要求,甚至不需要遵循特定的随机分布。所以 GUID 可能是最简单的方法。 对于 any PRNG 来说,“给定初始状态,可以预测到下一个 250 000 个 GUID”的声明似乎是一个天生正确的声明......我相信它是也不安全,但我不确定生成真正随机的 URL 有多大价值,如果这就是 OP 的目的的话。 ;) (无论如何+1 -- PRNG 教育很重要。)【参考方案6】:使用 Guid 将是一个很好的方法,但要获得类似于您的示例的内容,您可能需要将其转换为 Base64 字符串:
Guid g = Guid.NewGuid();
string GuidString = Convert.ToBase64String(g.ToByteArray());
GuidString = GuidString.Replace("=","");
GuidString = GuidString.Replace("+","");
为了更接近您的示例,我去掉了“=”和“+”,否则您的字符串末尾会出现“==”,中间会出现“+”。这是一个示例输出字符串:
“OZVV5TpP4U6wJthaCORZEQ”
【讨论】:
你也应该考虑替换 /。 不应将 Guid 视为安全随机字符串,因为可以猜测序列。 Guid 旨在避免密钥冲突,而不是随机的。堆栈溢出时有一些好的discussions of the randomness of a Guid。 要清楚简短地解释Convert.ToBase64String
的含义,take a look here。
可以将guid转换为base64并替换+和=增加碰撞概率吗?
@SimonEjsing 如果您真的可以编写一个应用程序,该应用程序在使用new Guid()
时不会发生“黑客攻击”(篡改时钟或内部 Windows 数据结构),我会邀请您喝杯啤酒。随意使用尽可能多的内核、线程、同步原语等。【参考方案7】:
2016 年 1 月 23 日更新
如果你觉得这个答案有用,你可能对a simple (~500 SLOC) password generation library I published感兴趣:
Install-Package MlkPwgen
然后你可以像下面的答案一样生成随机字符串:
var str = PasswordGenerator.Generate(length: 10, allowed: Sets.Alphanumerics);
该库的一个优点是代码可以更好地分解,因此您可以使用安全随机性for more than generating strings。查看the project site了解更多详情。
原答案
由于还没有人提供安全代码,因此我发布以下内容以防有人发现它有用。
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();
感谢 Ahmad 指出如何让代码在 .NET Core 上运行。
【讨论】:
@Keltex 解决方案不适用于 meh(它在几次使用后返回相同的字符串)。这个解决方案完美:) @LeeGrissom,偏见是一个重要方面。例如,假设您的字母表包含 255 个字符,并且您得到一个介于 0-255 之间的随机值。在环形缓冲区中,值 0 和 255 都对应于相同的字符,这会使结果偏向于字母表中的第一个字符,它的随机性会降低。当然,这是否重要取决于应用程序。 谁的目标是.netcore
:将var rng = new RNGCryptoServiceProvider()
替换为var rng = RandomNumberGenerator.Create()
为什么要计算'var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);'每次迭代?您可以在“使用”之前计算它。
@BartCalixto 已修复。谢谢!【参考方案8】:
我简化了@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();
【讨论】:
【参考方案9】: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
【讨论】:
【参考方案10】:如果您想要包含小写 和 大写字符 ([a-zA-Z0-9]) 的字母数字字符串,您可以使用 Convert.ToBase64String() 获得快速而简单的解决方案。 p>
至于唯一性,请查看birthday problem 来计算发生冲突的可能性(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)
【讨论】:
【参考方案11】:试试 Guid 和 Time.Ticks 的组合
var randomNumber = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) + DateTime.Now.Ticks;
randomNumber = System.Text.RegularExpressions.Regex.Replace(randomNumber, "[^0-9a-zA-Z]+", "");
【讨论】:
【参考方案12】:这很适合我
private string GeneratePasswordResetToken()
string token = Guid.NewGuid().ToString();
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(token);
return Convert.ToBase64String(plainTextBytes);
【讨论】:
【参考方案13】:我很惊讶为什么没有 CrytpoGraphic 解决方案。 GUID 是唯一的,但不是加密安全的。 See this Dotnet Fiddle.
var bytes = new byte[40]; // byte size
using (var crypto = new RNGCryptoServiceProvider())
crypto.GetBytes(bytes);
var base64 = Convert.ToBase64String(bytes);
Console.WriteLine(base64);
如果您想在前面添加 Guid:
var result = Guid.NewGuid().ToString("N") + base64;
Console.WriteLine(result);
更简洁的字母数字字符串:
result = Regex.Replace(result,"[^A-Za-z0-9]","");
Console.WriteLine(result);
【讨论】:
【参考方案14】:我在 Windows 上使用 Linux 命令的一站式解决方案是 scoop。 从scoop.sh安装scoop
scoop install openssl
openssl rand -base64 32
Dca3c3pptVkcb8fx243wN/3f/rQxx/rWYL8y7rZrGrA=
【讨论】:
投了反对票,因为这个答案与 C# 无关。它可能会在不同的问题上找到更好的归宿。 那里几乎没有注意到 c# 标签。以上是关于唯一的随机字符串生成的主要内容,如果未能解决你的问题,请参考以下文章