.net 类似YouTube的GUID

628mspwn  于 2022-12-24  发布在  .NET
关注(0)|答案(9)|浏览(162)

是否可以像YouTube(N7Et6c9nL9w)那样生成短GUID?
怎么做呢?我想在网络应用中使用。

nzk0hqpo

nzk0hqpo1#

您可以使用Base64:

string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

这会生成一个类似E1HKfn68Pkms5zsZsvKONw==的字符串,因为GUID总是128位,所以你可以省略==,因为你知道它总是出现在最后,这样你就给予了一个22个字符的字符串,虽然这不像YouTube那么短。

oyt4ldly

oyt4ldly2#

URL友好解决方案

正如已接受的答案中提到的,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友好字符(我们没有使用.和~),所以理论上,我们可以使用base66来获得

字符,即21.17,这也四舍五入为22。
因此,这对于完整、有效的GUID是最佳的。
然而,GUID使用6位来表示版本和变体,在我们的例子中是恒定的。因此,从技术上讲,我们只需要122位,在两个碱基中舍入为21(

= 20.33)。因此,通过更多的操作,我们可以删除另一个字符。然而,这需要处理这些位,所以我将其留给读者作为练习。
youtube是怎么做到的?
YouTube ID使用11个字符,他们是如何做到的?
一个GUID使用122位,这保证了冲突几乎是不可能的。这意味着你可以生成一个随机的GUID,并确保它是唯一的,而不需要检查。然而,我们不需要这么多的位只是一个普通的ID。
我们可以使用较小的ID。如果我们使用66位或更少,则冲突的风险更高,但可以用11个字符表示此ID(即使在base64中)。您可以接受冲突的风险,或者测试冲突并重新生成。
对于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)。如果使用此方法,您应该更加小心冲突。

rnmwe5a2

rnmwe5a23#

9个字符不是GUID,考虑到这一点,你可以使用int的十六进制表示,它给你一个8个字符的字符串。
你可以使用一个你可能已经有的id,你也可以用.GetHashCode来处理不同的简单类型,这样你就有了不同的int,你也可以异或不同的字段,如果你喜欢的话,你甚至可以使用一个随机数--嘿,如果你坚持正面的话,你有比2.000.000.000+更多的可能值;)

zvokhttg

zvokhttg4#

它不是GUID,而是自动递增的唯一字母数字字符串

请看下面的代码,我尝试做同样的事情,它使用EPOCHTotalMilliseconds和一个有效的字符集来生成一个唯一的字符串,该字符串随着每经过一毫秒而递增。
另一种方法是使用numeric counters,但维护成本很高,并且会创建一个系列,您可以使用+-值来猜测系统中的上一个或下一个唯一字符串,我们不知道会发生什么。

记住
*它不是全局唯一的,但对于定义它的示例是唯一的
*使用Thread.Sleep()处理多线程问题

public string YoutubeLikeId()
{
    Thread.Sleep(1);//make everything unique while looping
    long ticks = (long)(DateTime.UtcNow
    .Subtract(new DateTime(1970, 1, 1,0,0,0,0))).TotalMilliseconds;//EPOCH
    char[] baseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    .ToCharArray();
    
    int i = 32;
    char[] buffer = new char[i];
    int targetBase= baseChars.Length;

    do{
        buffer[--i] = baseChars[ticks % targetBase];
        ticks = ticks / targetBase;
    }
    while (ticks > 0);

    char[] result = new char[32 - i];
    Array.Copy(buffer, i, result, 0, 32 - i);

    return new string(result);
}

输出如下所示

XOTgBsu
XOTgBtB
XOTgBtR
XOTgBtg
XOTgBtw
XOTgBuE

**更新:**同样可以从Guid实现

var guid = Guid.NewGuid(); 
guid.ToString("N");
guid.ToString("N").Substring(0,8);
guid.ToString("N").Substring(8,4);
guid.ToString("N").Substring(12,4);
guid.ToString("N").Substring(16,4);
guid.ToString("N").Substring(20,12);

对于Guid ecd65132-ab5a-4587-87b8-b875e2fe0f35,它会将其分解为ecd65132ab5a458787b8b875e2fe0f35等块
但不能保证它总是唯一的。

**更新2:**还有一个名为ShortGuid的项目,用于获取url友好的GUID,它可以从常规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;
}

因此,Guid 4039124b-6153-4721-84dc-f56f5b057ac2将被编码为SxI5QFNhIUeE3PVvWwV6wg,输出将类似于。

ANf-MxRHHky2TptaXBxcwA
zpjp-stmVE6ZCbOjbeyzew
jk7P-XYFokmqgGguk_530A
81t6YZtkikGfLglibYkDhQ
qiM2GmqCK0e8wQvOSn-zLA
qc6wkl3g

qc6wkl3g5#

正如其他人提到的,YouTube的VideoId在技术上不是GUID,因为它本质上不是唯一的。
根据Wikipedia
唯一密钥的总数是2128或3.4 × 1038。这个数字是如此之大,以至于同一个数字被随机生成两次的概率可以忽略不计。
YouTube的VideoId的唯一性由其生成器算法保持。
您可以编写自己的算法,也可以使用某种随机字符串生成器并利用SQL中的UNIQUE CONSTRAINT约束来强制其唯一性。
首先,在数据库中创建一个UNIQUE CONSTRAINT

ALTER TABLE MyTable
ADD CONSTRAINT UniqueUrlId
UNIQUE (UrlId);

然后,例如,生成一个随机字符串(根据philipproplesch的答案):

string shortUrl = System.Web.Security.Membership.GeneratePassword(11, 0);

如果生成的UrlId足够随机且足够长,您应该很少会遇到SQL遇到重复的UrlId时引发的异常。在这种情况下,您可以在Web应用中轻松处理该异常。

elcex8rz

elcex8rz6#

Youtube有一个简单的随机字符串生成器,你可以使用一个允许使用的字符数组和一个随机数生成器在几分钟内完成。

eeq64g8w

eeq64g8w7#

这可能不是最好的解决方案,但您可以这样做:

string shortUrl = System.Web.Security.Membership.GeneratePassword(11, 0);
mefy6pfw

mefy6pfw8#

此ID可能不是全局唯一的。GUID应该是全局唯一的,因为它们包含不应出现在其他位置的元素(生成ID的计算机的MAC地址、生成ID的时间等)。
如果你需要的是一个在你的应用程序中唯一的ID,使用一个数字喷泉--也许将值编码为十六进制数。每次你需要一个ID时,从数字喷泉中获取它。
如果你有多个服务器分配id,你可以抓取一系列的数字(几十或几千取决于你分配id的速度),这应该可以完成工作。一个8位十六进制数将给予你40亿个id-但你的第一个id会短得多。

bvjxkvbb

bvjxkvbb9#

也许使用NanoId会让您省去很多麻烦:https://github.com/codeyu/nanoid-net
您可以执行以下操作:

var id = Nanoid.Generate('1234567890abcdef', 10) //=> "4f90d13a42"

你可以在这里检查碰撞概率:https://alex7kom.github.io/nano-nanoid-cc/

相关问题