.NET短唯一标识符

133

我需要在.NET中使用一个唯一标识符(由于太长,无法使用GUID)。

人们认为这里使用的算法是一个好的选择,还是您有其他建议?


8
有多短?有多独一无二?如果基于以太网适配器的硬件地址,GUID就能保证是独一无二的;纯数学计算生成的任何东西都不能被证明是独一无二的——只是极有可能独一无二(概率极高)。 - Jon
以下是有关编程的内容,请将其从英语翻译成中文。仅返回翻译后的文本:长度为15,并尽可能独特。 - Noel
6
长度是15个什么?15个字节吗?如果是的话,为什么不从GUID中去掉一个字节呢? - KristoferA
如果您不需要超过63位密钥,可以使用长整型获得相同的结果 https://github.com/joshclark/Flakey - Chris Marisic
4
在GUID中删除一个字节会极大地增加密钥冲突的可能性。如果您删除了错误顺序的字节,那么它可能会使密钥冲突变成必然发生的情况。 - Chris Marisic
请在此处查看我的答案:https://dev59.com/w3M_5IYBdhLWcg3wQQpd#56291295 - Vinod Srivastav
22个回答

142

这是一个不错的方法 - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx

还有一个方法可以参考:类似于YouTube的GUID

你可以使用Base64:

string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
那会生成一个类似于E1HKfn68Pkms5zsZsvKONw==的字符串。由于GUID始终为128位,因此您可以省略您知道将始终存在于末尾的==,这将给您一个22个字符长的字符串。虽然这不如YouTube短。

19
简要说明:如果需要用于URL,使用者可能还需要对"+"和"/"字符进行清理处理。 - tkit
2
我想在Unity中为UnnyNet创建一个最大长度为23个字符的唯一标识符,但是我被我的大而愚蠢的GUID卡住了,而你让我非常开心 :) - nipunasudha
有趣的是(如果你正在使用Zoom API),这几乎肯定是他们生成UUID的方式。它们有22个字符长,并且是base64编码的。 - vgel
简单而非常有帮助,谢谢! - TDiblik

57

我使用与Dor Cohen类似的方法,但删除了一些特殊字符:

var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");     

这将仅输出字母数字字符。UID(唯一标识符)的长度不能保证始终相同。以下是示例运行:

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg

17
如果丢弃这样的信息,您将失去GUID所保证的某些属性。我建议将您不舒服的字符替换为不同的字符,同时保持GUID和其序列化格式之间的双射。 - Lukáš Lánský
4
如果您不想将其转换回GUID,但需要随机字符用于其他目的,例如在多线程中跟踪工作人员或为线程化对象添加日志前缀等,那么这是一个不错的选择。 - Piotr Kula

49
var ticks = new DateTime(2016,1,1).Ticks;
var ans = DateTime.Now.Ticks - ticks;
var uniqueId = ans.ToString("x");

保留一个基准日期(在此示例中为2016年1月1日),从该日期开始生成这些ID。这将使您的ID更小。

生成的编号:3af3c14996e54


DateTime 对象的 milliseconds 始终为0。 - Teejay
也请删除最后一句话。 - Teejay
2
一行代码: var uniqueId = (DateTime.Now.Ticks - new DateTime(2016, 1, 1).Ticks).ToString("x"); - Sgedda
14
不适合使用for循环同时生成几乎相同的id号,例如https://dotnetfiddle.net/L3MIgZ。 - Jaider
刚刚在本地尝试了一下 DateTime.Now.Ticks,发现它从未返回相同的值。为什么 dotnetfiddle 的行为不同呢? - Eugene Shelukhin
显示剩余3条评论

33

简单易用的包。我将其用于生成临时请求ID。

https://www.nuget.org/packages/shortid

https://github.com/bolorundurowb/shortid

使用了 System.Random

string id = ShortId.Generate();
// id = KXTR_VzGVUoOY
如果你希望通过指定数字、特殊字符和长度来控制生成的ID类型,请调用Generate方法并传递三个参数:第一个布尔值表示是否需要数字,第二个布尔值表示是否需要特殊字符,最后一个数字表示您的长度偏好。
string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w

11
据我所知,仅仅去掉 GUID 的一部分并不能保证唯一性,实际上远远不足以保证唯一性。
我所了解的保证全局唯一性的最短方法可以在 Jeff Atwood 的博客文章(链接)中找到。在这篇文章中,他讨论了多种缩短 GUID 的方法,并通过 ASCII85 编码将其缩短至20字节。
然而,如果您需要一个长度不超过15字节的解决方案,恐怕您别无选择,只能使用某些无法保证全局唯一性的东西。

11

如果仅仅移除连字符就足够了,以下内容或许对您有帮助:

Guid.NewGuid().ToString("n")

这会生成32个字符的完全唯一字符串:
5db4cee3bfd8436395d37fca2d48d5b3
82fac271c76148a3a0667c00a5da990d

8

对于我的本地应用程序,我使用基于时间的方法:

/// <summary>
/// Returns all ticks, milliseconds or seconds since 1970.
/// 
/// 1 tick = 100 nanoseconds
/// 
/// Samples:
/// 
/// Return unit     value decimal           length      value hex       length
/// --------------------------------------------------------------------------
/// ticks           14094017407993061       17          3212786FA068F0  14
/// milliseconds    1409397614940           13          148271D0BC5     11
/// seconds         1409397492              10          5401D2AE        8
///
/// </summary>
public static string TickIdGet(bool getSecondsNotTicks, bool getMillisecondsNotTicks, bool getHexValue)
{
    string id = string.Empty;

    DateTime historicalDate = new DateTime(1970, 1, 1, 0, 0, 0);

    if (getSecondsNotTicks || getMillisecondsNotTicks)
    {
        TimeSpan spanTillNow = DateTime.UtcNow.Subtract(historicalDate);

        if (getSecondsNotTicks)
            id = String.Format("{0:0}", spanTillNow.TotalSeconds);
        else
            id = String.Format("{0:0}", spanTillNow.TotalMilliseconds);
    }
    else
    {
        long ticksTillNow = DateTime.UtcNow.Ticks - historicalDate.Ticks;
        id = ticksTillNow.ToString();
    }

    if (getHexValue)
        id = long.Parse(id).ToString("X");

    return id;
}

6

在数据库中,IDENTITY值应该是唯一的,但您应该意识到其限制...例如,它使得批量数据插入基本上不可能,如果您正在处理大量记录,则会减慢速度。

您还可以使用日期/时间值。我见过几个数据库将日期/时间用作主键,虽然这不是非常干净的方法,但它确实有效。如果您控制插入操作,则可以在代码中有效地保证值是唯一的。


5

参考其他方案,这里是我的解决方案,可以提供一个不同的编码GUID,它是URL(和Docker)安全的,不会丢失任何信息:

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");

示例输出如下:

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig

它用一些替代字符来取代在URL或Docker名称中需要的值的情况。由于进行了替换,您不会丢失任何信息。 - Roemer

5

22个字符,URL安全,并保持Guid的唯一性。

// Our url safe, base 64 alphabet:
const string alphabet = "-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

// Sanitized Guid string. Preserve the last two hex chars
var guidStr = "929F7C4D4B2644E1A122A379C02D6345";
var lastTwo = guidStr.Substring(30, 2);

string shortGuid = "";

// Iterate over the ten groups of 3 hex chars: 929 F7C 4D4 B26 44E 1A1 22A 379 C02 D63
for (var i = 0; i < 10; i++)
{
    var hex = guidStr.Substring(i*3, 3);              // Get the next 3 hex chars
    var x = Convert.ToInt32(hex, 16);                 // Convert to int
    shortGuid += $"{alphabet[x/64]}{alphabet[x%64]}"; // Lookup the two-digit base64 value
}
shortGuid += lastTwo; // Don't forget the last two

Console.WriteLine(shortGuid);

输出:

yDXWhiGAfc4v6EbTK0Px45

不是点踩者,但不确定这与被接受的答案有什么区别,它只是一种不需要显式转换为Base64的不同方法。 - phuzi
2
我的意图是提供一个展示算法的解决方案。显然,有更少的代码行数可以实现同样的功能。但了解事物的工作原理总是好的。这个解决方案可以轻松地移植到任何其他编程语言中。 - Didaxis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接