创建一个唯一的 5 个字符的字母数字字符串
Create a UNIQUE 5 character alphanumeric string
我们正在考虑创建促销代码以发送给客户,我们被告知发送的每个代码必须是唯一的 - 5 个字符 - 字母数字。
我考虑过对连接的字符串进行散列处理并取散列的前 5 个字符,但很可能会一次又一次出现相同的 5 个字符。
任何人都可以给我任何关于创建这个每次都是唯一的 5 字符字母数字字符串的指示吗?
我不久前想到了这个,它可能会满足您的需求。
/// <summary>
/// Return a string of random hexadecimal values which is 6 characters long and relatively unique.
/// </summary>
/// <returns></returns>
/// <remarks>In testing, result was unique for at least 10,000,000 values obtained in a loop.</remarks>
public static string GetShortID()
{
var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider();
var bytes = new byte[5];
crypto.GetBytes(bytes); // get an array of random bytes.
return BitConverter.ToString(bytes).Replace("-", string.Empty); // convert array to hex values.
}
我理解您的要求是 "must" 是唯一的,但请记住,唯一性充其量只是一个相对概念。甚至我们的老朋友 the GUID 也不是 真正的 独一无二的:
...the probability of the same number being generated randomly twice
is negligible
如果我没记错的话,我发现我的代码不是 100% 唯一的,只有 5 个字符经过许多次迭代(数十万次或可能低数百万次——我记不太清楚了),但在测试中对于 6,结果 是 对于在循环中获得的至少 10,000,000 个值是唯一的。
您可以在长度为 5 时自行测试并确定它是否足够独特以达到您的目的。如果需要,只需将 6
切换为 5
。
附录: 其他一些提醒我您可能需要考虑线程安全。这是一种修改后的方法:
private static object _lock = new object();
/// <summary>
/// Return a string of random hexadecimal values which is 6 characters long and relatively unique.
/// </summary>
/// <returns></returns>
/// <remarks>In testing, result was unique for at least 10,000,000 values obtained in a loop.</remarks>
public static string GetShortID()
{
lock(_lock)
{
var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider();
var bytes = new byte[5];
crypto.GetBytes(bytes); // get an array of random bytes.
return BitConverter.ToString(bytes).Replace("-", string.Empty); // convert array to hex values.
}
}
我会创建一个包含所有可能的 5 位字符串的 table (PromoCode)。然后用两个字段创建另一个 table CampaignPromoCode:
Code varchar(5), CampaignId uniqueidentifier
这样您就可以跟踪每个广告系列的使用情况。要获得随机未使用的促销代码,请使用此语句:
select top 1 Code
from PromoCode pc
left join CampaignPromoCode cpc on cpc.Code = pc.Code
where cpc.CampaignId = 'campaign-id-goes-here'
and cpc.Code is null
order by newid()
这里的底线是,您可能应该回到管理层并告诉他们强制 100% 绝对唯一性的要求是一项成本过高的要求,您只需花费一小部分成本即可获得 99.9999% 的唯一性.然后使用 roryap 的代码生成一个随机的,主要是唯一的代码。
正如我在其他回答的评论中提到的,它可能不足以满足您的目的。我编写了更多代码来生成一串 运行dom 字母数字字符。这一次,它们不限于 0-9 和 A-F——即 运行domly 生成的 nibbles 的十六进制等价物。相反,它们由完整的 运行ge 字母数字字符组成,至少是大写字母。这应该足以增加唯一性的可能性,因为我们从 16 个可能的十六进制字符到 36 个可能的完整字母和 0-9 字符。
不过,当我 运行 尝试超过 10,000,000 次时,还是出现了很多重复项。这只是野兽的本性:你用这么短的字符串被骗的可能性相当高。无论如何,它就在这里。你可以玩弄它。如果您的客户不介意小写字母——例如如果 "RORYAP" 与 "RoryAp" 不同——那么这甚至会进一步增加唯一性的可能性。
/// <summary>
/// Instances of this class are used to geneate alpha-numeric strings.
/// </summary>
public sealed class AlphaNumericStringGenerator
{
/// <summary>
/// The synchronization lock.
/// </summary>
private object _lock = new object();
/// <summary>
/// The cryptographically-strong random number generator.
/// </summary>
private RNGCryptoServiceProvider _crypto = new RNGCryptoServiceProvider();
/// <summary>
/// Construct a new instance of this class.
/// </summary>
public AlphaNumericStringGenerator()
{
//Nothing to do here.
}
/// <summary>
/// Return a string of the provided length comprised of only uppercase alpha-numeric characters each of which are
/// selected randomly.
/// </summary>
/// <param name="ofLength">The length of the string which will be returned.</param>
/// <returns>Return a string of the provided length comprised of only uppercase alpha-numeric characters each of which are
/// selected randomly.</returns>
public string GetRandomUppercaseAlphaNumericValue(int ofLength)
{
lock (_lock)
{
var builder = new StringBuilder();
for (int i = 1; i <= ofLength; i++)
{
builder.Append(GetRandomUppercaseAphanumericCharacter());
}
return builder.ToString();
}
}
/// <summary>
/// Return a randomly-generated uppercase alpha-numeric character (A-Z or 0-9).
/// </summary>
/// <returns>Return a randomly-generated uppercase alpha-numeric character (A-Z or 0-9).</returns>
private char GetRandomUppercaseAphanumericCharacter()
{
var possibleAlphaNumericValues =
new char[]{'A','B','C','D','E','F','G','H','I','J','K','L',
'M','N','O','P','Q','R','S','T','U','V','W','X','Y',
'Z','0','1','2','3','4','5','6','7','8','9'};
return possibleAlphaNumericValues[GetRandomInteger(0, possibleAlphaNumericValues.Length - 1)];
}
/// <summary>
/// Return a random integer between a lower bound and an upper bound.
/// </summary>
/// <param name="lowerBound">The lower-bound of the random integer that will be returned.</param>
/// <param name="upperBound">The upper-bound of the random integer that will be returned.</param>
/// <returns> Return a random integer between a lower bound and an upper bound.</returns>
private int GetRandomInteger(int lowerBound, int upperBound)
{
uint scale = uint.MaxValue;
// we never want the value to exceed the maximum for a uint,
// so loop this until something less than max is found.
while (scale == uint.MaxValue)
{
byte[] fourBytes = new byte[4];
_crypto.GetBytes(fourBytes); // Get four random bytes.
scale = BitConverter.ToUInt32(fourBytes, 0); // Convert that into an uint.
}
var scaledPercentageOfMax = (scale / (double) uint.MaxValue); // get a value which is the percentage value where scale lies between a uint's min (0) and max value.
var range = upperBound - lowerBound;
var scaledRange = range * scaledPercentageOfMax; // scale the range based on the percentage value
return (int) (lowerBound + scaledRange);
}
}
我们正在考虑创建促销代码以发送给客户,我们被告知发送的每个代码必须是唯一的 - 5 个字符 - 字母数字。
我考虑过对连接的字符串进行散列处理并取散列的前 5 个字符,但很可能会一次又一次出现相同的 5 个字符。
任何人都可以给我任何关于创建这个每次都是唯一的 5 字符字母数字字符串的指示吗?
我不久前想到了这个,它可能会满足您的需求。
/// <summary>
/// Return a string of random hexadecimal values which is 6 characters long and relatively unique.
/// </summary>
/// <returns></returns>
/// <remarks>In testing, result was unique for at least 10,000,000 values obtained in a loop.</remarks>
public static string GetShortID()
{
var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider();
var bytes = new byte[5];
crypto.GetBytes(bytes); // get an array of random bytes.
return BitConverter.ToString(bytes).Replace("-", string.Empty); // convert array to hex values.
}
我理解您的要求是 "must" 是唯一的,但请记住,唯一性充其量只是一个相对概念。甚至我们的老朋友 the GUID 也不是 真正的 独一无二的:
...the probability of the same number being generated randomly twice is negligible
如果我没记错的话,我发现我的代码不是 100% 唯一的,只有 5 个字符经过许多次迭代(数十万次或可能低数百万次——我记不太清楚了),但在测试中对于 6,结果 是 对于在循环中获得的至少 10,000,000 个值是唯一的。
您可以在长度为 5 时自行测试并确定它是否足够独特以达到您的目的。如果需要,只需将 6
切换为 5
。
附录: 其他一些提醒我您可能需要考虑线程安全。这是一种修改后的方法:
private static object _lock = new object();
/// <summary>
/// Return a string of random hexadecimal values which is 6 characters long and relatively unique.
/// </summary>
/// <returns></returns>
/// <remarks>In testing, result was unique for at least 10,000,000 values obtained in a loop.</remarks>
public static string GetShortID()
{
lock(_lock)
{
var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider();
var bytes = new byte[5];
crypto.GetBytes(bytes); // get an array of random bytes.
return BitConverter.ToString(bytes).Replace("-", string.Empty); // convert array to hex values.
}
}
我会创建一个包含所有可能的 5 位字符串的 table (PromoCode)。然后用两个字段创建另一个 table CampaignPromoCode:
Code varchar(5), CampaignId uniqueidentifier
这样您就可以跟踪每个广告系列的使用情况。要获得随机未使用的促销代码,请使用此语句:
select top 1 Code
from PromoCode pc
left join CampaignPromoCode cpc on cpc.Code = pc.Code
where cpc.CampaignId = 'campaign-id-goes-here'
and cpc.Code is null
order by newid()
这里的底线是,您可能应该回到管理层并告诉他们强制 100% 绝对唯一性的要求是一项成本过高的要求,您只需花费一小部分成本即可获得 99.9999% 的唯一性.然后使用 roryap 的代码生成一个随机的,主要是唯一的代码。
正如我在其他回答的评论中提到的,它可能不足以满足您的目的。我编写了更多代码来生成一串 运行dom 字母数字字符。这一次,它们不限于 0-9 和 A-F——即 运行domly 生成的 nibbles 的十六进制等价物。相反,它们由完整的 运行ge 字母数字字符组成,至少是大写字母。这应该足以增加唯一性的可能性,因为我们从 16 个可能的十六进制字符到 36 个可能的完整字母和 0-9 字符。
不过,当我 运行 尝试超过 10,000,000 次时,还是出现了很多重复项。这只是野兽的本性:你用这么短的字符串被骗的可能性相当高。无论如何,它就在这里。你可以玩弄它。如果您的客户不介意小写字母——例如如果 "RORYAP" 与 "RoryAp" 不同——那么这甚至会进一步增加唯一性的可能性。
/// <summary>
/// Instances of this class are used to geneate alpha-numeric strings.
/// </summary>
public sealed class AlphaNumericStringGenerator
{
/// <summary>
/// The synchronization lock.
/// </summary>
private object _lock = new object();
/// <summary>
/// The cryptographically-strong random number generator.
/// </summary>
private RNGCryptoServiceProvider _crypto = new RNGCryptoServiceProvider();
/// <summary>
/// Construct a new instance of this class.
/// </summary>
public AlphaNumericStringGenerator()
{
//Nothing to do here.
}
/// <summary>
/// Return a string of the provided length comprised of only uppercase alpha-numeric characters each of which are
/// selected randomly.
/// </summary>
/// <param name="ofLength">The length of the string which will be returned.</param>
/// <returns>Return a string of the provided length comprised of only uppercase alpha-numeric characters each of which are
/// selected randomly.</returns>
public string GetRandomUppercaseAlphaNumericValue(int ofLength)
{
lock (_lock)
{
var builder = new StringBuilder();
for (int i = 1; i <= ofLength; i++)
{
builder.Append(GetRandomUppercaseAphanumericCharacter());
}
return builder.ToString();
}
}
/// <summary>
/// Return a randomly-generated uppercase alpha-numeric character (A-Z or 0-9).
/// </summary>
/// <returns>Return a randomly-generated uppercase alpha-numeric character (A-Z or 0-9).</returns>
private char GetRandomUppercaseAphanumericCharacter()
{
var possibleAlphaNumericValues =
new char[]{'A','B','C','D','E','F','G','H','I','J','K','L',
'M','N','O','P','Q','R','S','T','U','V','W','X','Y',
'Z','0','1','2','3','4','5','6','7','8','9'};
return possibleAlphaNumericValues[GetRandomInteger(0, possibleAlphaNumericValues.Length - 1)];
}
/// <summary>
/// Return a random integer between a lower bound and an upper bound.
/// </summary>
/// <param name="lowerBound">The lower-bound of the random integer that will be returned.</param>
/// <param name="upperBound">The upper-bound of the random integer that will be returned.</param>
/// <returns> Return a random integer between a lower bound and an upper bound.</returns>
private int GetRandomInteger(int lowerBound, int upperBound)
{
uint scale = uint.MaxValue;
// we never want the value to exceed the maximum for a uint,
// so loop this until something less than max is found.
while (scale == uint.MaxValue)
{
byte[] fourBytes = new byte[4];
_crypto.GetBytes(fourBytes); // Get four random bytes.
scale = BitConverter.ToUInt32(fourBytes, 0); // Convert that into an uint.
}
var scaledPercentageOfMax = (scale / (double) uint.MaxValue); // get a value which is the percentage value where scale lies between a uint's min (0) and max value.
var range = upperBound - lowerBound;
var scaledRange = range * scaledPercentageOfMax; // scale the range based on the percentage value
return (int) (lowerBound + scaledRange);
}
}