是否可能生成像YouTube中的短GUID(例如N7Et6c9nL9w)?
如何实现?我想在Web应用程序中使用它。
是否可能生成像YouTube中的短GUID(例如N7Et6c9nL9w)?
如何实现?我想在Web应用程序中使用它。
你可以使用Base64:
string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
这将生成一个类似于E1HKfn68Pkms5zsZsvKONw==
的字符串。由于GUID始终为128位,因此可以省略你知道将始终出现在末尾的==
,从而得到一个22个字符长的字符串。但这不如YouTube的短。
如接受的答案所述,base64是一个不错的解决方案,但如果您想在URL中使用GUID,则可能会出现问题。这是因为+和/是有效的base64字符,但在URL中具有特殊含义。
幸运的是,在base64中有一些未使用的字符是URL友好的。以下是更完整的答案:
public string ToShortString(Guid guid)
{
var base64Guid = Convert.ToBase64String(guid.ToByteArray());
// Replace URL unfriendly characters
base64Guid = base64Guid.Replace('+', '-').Replace('/', '_');
// Remove the trailing ==
return base64Guid.Substring(0, base64Guid.Length - 2);
}
public Guid FromShortString(string str)
{
str = str.Replace('_', '/').Replace('-', '+');
var byteArray = Convert.FromBase64String(str + "==");
return new Guid(byteArray);
}
使用方法:
Guid guid = Guid.NewGuid();
string shortStr = ToShortString(guid);
// shortStr will look something like 2LP8GcHr-EC4D__QTizUWw
Guid guid2 = FromShortString(shortStr);
Assert.AreEqual(guid, guid2);
编辑:
上面的方法可以生成22个字符、友好的URL GUID。 这是因为GUID使用了128位,所以在base64中表示需要 个字符,即21.33,四舍五入为22。
实际上有 66 个 URL 友好的字符(我们不使用 . and ~)。因此,从理论上讲,我们可以使用 base66 来获得 个字符,即21.17,也四舍五入为22。
因此,这对于完整的有效 GUID 是最优的。
然而,GUID 使用了 6 位来表示版本和变体,在我们的情况下是固定的。因此,我们技术上只需要 122 位,在两个基数中都是 21 ( = 20.33)。因此,通过更多的操作,我们可以再减少一个字符。这需要处理位数,所以我把这留给读者练习。
YouTube ID 使用了11个字符。他们是如何做到的?
GUID 使用了122位,这保证了碰撞几乎不可能发生。这意味着你可以生成一个随机 GUID,并且肯定是唯一的而无需检查。但是,我们对于普通 ID 并不需要这么多的位数。
我们可以使用较小的 ID。如果我们使用 66 位或更少的位数,就有更高的碰撞风险,但可以用 11 个字符(甚至在 base64 中)表示此 ID。你可以接受碰撞的风险,或者测试并重新生成。
使用 122 位(常规 GUID),你需要生成约 个 GUID 才能有 1% 的碰撞率。
使用 66 位,则需要生成约 或 10 亿个 ID 才有 1% 的碰撞几率。那不是很多 ID。
我猜测 YouTube 使用了 64 位(比 66 位更节省内存),并检查碰撞以重新生成 ID,如果必要的话。
如果你想放弃 GUID,转而使用较小的 ID,则可以使用以下代码:
class IdFactory
{
private Random random = new Random();
public int CharacterCount { get; }
public IdFactory(int characterCount)
{
CharacterCount = characterCount;
}
public string Generate()
{
// bitCount = characterCount * log (targetBase) / log(2)
var bitCount = 6 * CharacterCount;
var byteCount = (int)Math.Ceiling(bitCount / 8f);
byte[] buffer = new byte[byteCount];
random.NextBytes(buffer);
string guid = Convert.ToBase64String(buffer);
// Replace URL unfriendly characters
guid = guid.Replace('+', '-').Replace('/', '_');
// Trim characters to fit the count
return guid.Substring(0, CharacterCount);
}
}
用法:
var factory = new IdFactory(characterCount: 11);
string guid = factory.Generate();
// guid will look like Mh3darwiZhp
这使用了64个字符,虽然不是最佳选择,但需要的代码量较少(因为我们可以重复使用Convert.ToBase64String)。 如果您使用此操作,请更加小心碰撞。
base64Guid.Substring(0, 22)
而不是base64Guid.Substring(0, base64Guid.Length - 2)
呢? - crushGuid.NewGuid().ToString("N")
呢? - Vinod Srivastav9个字符不是GUID。鉴于此,您可以使用int的十六进制表示形式,从而获得8个字符的字符串。
您可以使用可能已经拥有的ID。此外,您可以针对不同的简单类型使用.GetHashCode方法,从而获得不同的int。您还可以异或不同的字段。如果您感兴趣,甚至可以使用随机数 - 嘿,如果您坚持使用正值,您将拥有超过20亿个可能的值;)
GUID
,而是一个随机的base64
编码字符串那么关于冲突呢? 由于他们拥有庞大的基础设施和遍布全球的服务器,在每分钟上传数千个视频的情况下,这是一种安全的方式。在将您的视频后处理为不同分辨率期间,系统可以进行检查。
请参见以下代码,我正在尝试相同的操作,它使用EPOCH
的TotalMilliseconds
生成带有有效字符集的字符串,其独特性由每过去的毫秒数控制。这是基于来自 Tom Scott的视频的思路。
另一种方法是使用数字计数器
,但这种方法维护起来很耗费,并且会创建一个系列,可以枚举与系统中以前或下一个唯一字符串的+
或-
值,我们不希望发生这种情况。
Thread.Sleep()
来处理多线程问题public static string YoutubeLikeId()
{
Thread.Sleep(1);
string characterSet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var charSet = characterSet.ToCharArray();
int targetBase= charSet.Length;
long ticks = (long)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string output = null;
do{
output += charSet[ticks % targetBase];
ticks = ticks/targetBase;
}while(ticks > 0);
output = new string(output.Reverse().ToArray());
return Convert.ToBase64String(Encoding.UTF8.GetBytes(output)).Replace("/", "_")
.Replace("+", "-").Replace("==","");
}
在这里,我们将删除填充字符==
以及非URL友好字符/
和+
,并用-
和_
替换它们。
因此,输出将类似于:
VFlRTFk4Mw
VFlRTFk4SQ
VFlRTFk4WA
VFlRTFk4bQ
VFlRTFk5Mg
VFlRTFk5SQ
VFlRTFk5WA
VFlRTFk5bg
VFlRTFlBMw
VFlRTFlBSQ
还有一个名为ShortGuid的项目,可以获得一个友好的URL GUID
,它可以从/转换为常规的Guid
当我深入了解时,我发现它通过将Guid
编码为Base64
来实现,如下所示:
public static string Encode(Guid guid)
{
string encoded = Convert.ToBase64String(guid.ToByteArray());
encoded = encoded
.Replace("/", "_")
.Replace("+", "-");
return encoded.Substring(0, 22);
}
它的好处是可以再次解码以获取Guid
public static Guid Decode(string value)
{
// avoid parsing larger strings/blobs
if (value.Length != 22)
{
throw new ArgumentException("A ShortGuid must be exactly 22 characters long. Receive a character string.");
}
string base64 = value
.Replace("_", "/")
.Replace("-", "+") + "==";
byte[] blob = Convert.FromBase64String(base64);
var guid = new Guid(blob);
var sanityCheck = Encode(guid);
if (sanityCheck != value)
{
throw new FormatException(
@"Invalid strict ShortGuid encoded string. The string '{value}' is valid URL-safe Base64, " +
@"but failed a round-trip test expecting '{sanityCheck}'."
);
}
return guid;
}
4039124b-6153-4721-84dc-f56f5b057ac2
将被编码为 SxI5QFNhIUeE3PVvWwV6wg
,输出将类似于以下内容。ANf-MxRHHky2TptaXBxcwA
zpjp-stmVE6ZCbOjbeyzew
jk7P-XYFokmqgGguk_530A
81t6YZtkikGfLglibYkDhQ
qiM2GmqCK0e8wQvOSn-zLA
正如Leonadro在评论中提到的那样,如果您不想自己实现,还有一个叫做nanoid的东西可以用于此目的。
ALTER TABLE MyTable
ADD CONSTRAINT UniqueUrlId
UNIQUE (UrlId);
例如,生成一个随机字符串(来自philipproplesch的答案):
string shortUrl = System.Web.Security.Membership.GeneratePassword(11, 0);
UrlId
足够随机且足够长,当SQL遇到重复的UrlId
时,你应该很少遇到抛出的异常。在这种情况下,您可以轻松处理Web应用程序中的异常。GeneratePassword
方法唯一的问题在于第二个参数实际上是用来指定非字母和数字字符最小数量的。当我尝试使用 0
时,会得到多个这样的符号... - Azimuth从技术上来说,这不是一个Guid。Youtube有一个简单的随机字符串生成器,您可以使用允许字符的数组和随机数生成器在几分钟内轻松创建它。
这可能不是最好的解决方案,但你可以尝试类似这样的方法:
string shortUrl = System.Web.Security.Membership.GeneratePassword(11, 0);
0
时,会得到几个这样的符号... - Azimuth也许使用NanoId可以帮你避免很多麻烦:
https://github.com/codeyu/nanoid-net你可以这样做:
var id = Nanoid.Generate('1234567890abcdef', 10) //=> "4f90d13a42"
你可以在这里检查碰撞概率: https://alex7kom.github.io/nano-nanoid-cc/