.NET短唯一标识符


91

我需要.NET中的唯一标识符(不能使用GUID,因为在这种情况下,它太长了)。

人们是否认为此处使用的算法是不错的选择,或者您还有其他建议吗?


3
多短?以及如何独特?如果基于以太网适配器的硬件地址,则GUID保证唯一。纯粹以数学方式产生的任何事物都不可能证明是唯一的-可能是唯一的(天文学上具有很高的可能性)。
2012年

长度为15,并且尽可能唯一(可能)
Noel 2012年

3
var random = 4; //足够好
KristoferA'2

3
15 什么长?15个字节?如果是这样,为什么不从GUID上删除一个字节呢?
KristoferA'2

3
@KristoferA天文数字地删除行进中的一个字节会增加按键碰撞的机会。如果剥离错误的有序字节,则可能会确定它发生冲突。
克里斯·马里西克

Answers:


96

这个不错-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短。


11
简短说明:如果URL需要使用此
标记


1
我希望在Unity中为UnnyNet最多保留23个字符长的唯一ID,并且我一直呆在我笨拙的GUID中,而您却让我如此高兴:)
nipunasudha

38

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

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

这将仅输出字母数字字符。不保证UID的长度始终相同。这是一个示例运行:

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg

6
通过丢弃这样的信息,您将失去GUID保证的某些属性。我建议您用其他字符替换您不习惯的字符,同时保留GUID及其序列化格式之间的双射。
卢卡斯兰斯基

3
这是一个很好的一个多线程的日志前缀螺纹对象等使用,如果你不想转换回一个GUID,但只需要别的东西随机字符。例如跟踪工人,或者
彼得·库拉

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

从开始生成这些ID的日期起算一个基准日期(在本例中为2016年1月1日)。这会使您的ID变小。

生成的编号:3af3c14996e54


millisecondsDateTime对象始终为0
Teejay

同时删除最后一句话。
Teejay

1
oneliner:var uniqueId =(DateTime.Now.Ticks-新的DateTime(2016,1,1).Ticks).ToString(“ x”);
Sgedda

4
几乎不像for-loop.eg
Jaider

16

简单易用的软件包。我将其用于时间请求ID生成器。

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

https://github.com/bolorundurowb/shortid

用途 System.Random

string id = ShortId.Generate();
// id = KXTR_VzGVUoOY

(来自github页面)

如果要通过指定是否要使用数字,特殊字符和长度来控制生成的id的类型,请调用Generate方法并传递三个参数,第一个是布尔值,说明是否需要数字,第二个是布尔值,说明是否需要数字特殊字符,最后一个数字表示您的长度偏好。

string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w

10

据我所知,仅剥离GUID的部分并不能保证是唯一的 -实际上,它远非唯一。

杰夫·阿特伍德(Jeff Atwood)这篇博客文章中介绍了我所知能保证全球唯一性的最短方法。在链接的文章中,他讨论了缩短GUID的多种方法,最后通过Ascii85编码将其缩减为20个字节。

但是,如果您绝对需要不超过15个字节的解决方案,那么恐怕您别无选择,只能使用无法保证全局唯一的东西。


6

IDENTITY值在数据库中应该是唯一的,但是您应该意识到其局限性……例如,它使批量数据插入基本上变得不可能,如果您要处理大量记录,这会降低速度。

您也许还可以使用日期/时间值。我已经看过几个数据库,它们使用日期/时间作为PK,虽然它不是超级干净的-但它可以工作。如果控制插入,则可以有效保证值在代码中是唯一的。


6

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

/// <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;
}

3

这是我的解决方案,对于并发来说并不安全,每秒不超过1000 GUID和线程安全。

public static class Extensors
{

    private static object _lockGuidObject;

    public static string GetGuid()
    {

        if (_lockGuidObject == null)
            _lockGuidObject = new object();


        lock (_lockGuidObject)
        {

            Thread.Sleep(1);
            var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var epochLong = Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);

            return epochLong.DecimalToArbitrarySystem(36);

        }

    }

    /// <summary>
    /// Converts the given decimal number to the numeral system with the
    /// specified radix (in the range [2, 36]).
    /// </summary>
    /// <param name="decimalNumber">The number to convert.</param>
    /// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
    /// <returns></returns>
    public static string DecimalToArbitrarySystem(this long decimalNumber, int radix)
    {
        const int BitsInLong = 64;
        const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        if (radix < 2 || radix > Digits.Length)
            throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());

        if (decimalNumber == 0)
            return "0";

        int index = BitsInLong - 1;
        long currentNumber = Math.Abs(decimalNumber);
        char[] charArray = new char[BitsInLong];

        while (currentNumber != 0)
        {
            int remainder = (int)(currentNumber % radix);
            charArray[index--] = Digits[remainder];
            currentNumber = currentNumber / radix;
        }

        string result = new String(charArray, index + 1, BitsInLong - index - 1);
        if (decimalNumber < 0)
        {
            result = "-" + result;
        }

        return result;
    }

代码未优化,仅提供示例!


虽然这是一个有趣的解决方案,但不能保证UtcNow每毫秒返回一个唯一的滴答值:根据这些备注,分辨率取决于系统计时器。此外,您最好确保系统时钟不会向后更改!(由于user13971889的答案将这个问题引到了我的摘要的顶部,并且我对该答案进行了评论,所以我认为我应该在这里重复该批评。)
Joe Sewell

3

如果您的应用没有几个百万富翁,使用它们会在SAME MILLISECOND生成短的唯一字符串,那么您可以考虑使用以下函数。

private static readonly Object obj = new Object();
private static readonly Random random = new Random();
private string CreateShortUniqueString()
{
    string strDate = DateTime.Now.ToString("yyyyMMddhhmmssfff");
    string randomString ;
    lock (obj)
    {
        randomString = RandomString(3);
    }
    return strDate + randomString; // 16 charater
}
private string RandomString(int length)
{

    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxy";
    var random = new Random();
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

如果您只需要在未来99年内使用您的应用,请将yyyy更改为yy。
更新20160511:正确的随机函数
-添加锁定对象
-将随机变量移出RandomString函数
Ref


1
这很棒,尽管您不应该每次都初始化一个新的Random-的原因lock是允许您重复使用同一Random实例。我认为您忘记删除该行!
NibblyPig

1

我知道离发布日期还很遥远... :)

我有一个只生成9个十六进制字符的生成器,例如:C9D6F7FF3,C9D6FB52C

public class SlimHexIdGenerator : IIdGenerator
{
    private readonly DateTime _baseDate = new DateTime(2016, 1, 1);
    private readonly IDictionary<long, IList<long>> _cache = new Dictionary<long, IList<long>>();

    public string NewId()
    {
        var now = DateTime.Now.ToString("HHmmssfff");
        var daysDiff = (DateTime.Today - _baseDate).Days;
        var current = long.Parse(string.Format("{0}{1}", daysDiff, now));
        return IdGeneratorHelper.NewId(_cache, current);
    }
}


static class IdGeneratorHelper
{
    public static string NewId(IDictionary<long, IList<long>> cache, long current)
    {
        if (cache.Any() && cache.Keys.Max() < current)
        {
            cache.Clear();
        }

        if (!cache.Any())
        {
            cache.Add(current, new List<long>());
        }

        string secondPart;
        if (cache[current].Any())
        {
            var maxValue = cache[current].Max();
            cache[current].Add(maxValue + 1);
            secondPart = maxValue.ToString(CultureInfo.InvariantCulture);
        }
        else
        {
            cache[current].Add(0);
            secondPart = string.Empty;
        }

        var nextValueFormatted = string.Format("{0}{1}", current, secondPart);
        return UInt64.Parse(nextValueFormatted).ToString("X");
    }
}

1

基于@dorcohen的回答和@pootzko的评论。您可以使用它。通过电线是安全的。

var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());

如果有人想知道,结果:Jzhw2oVozkSNa2IkyK4ilA2或尝试在dotnetfiddle.net/VIrZ8j
chriszo111 '18

1

基于其他方面,这是我的解决方案,它提供了不同的编码guid,该guid是URL(和Docker)安全的,并且不会丢失任何信息:

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

输出示例如下:

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig

1

在C#中,一个long值具有64位,如果使用Base64进行编码,则将有12个字符,其中包括1个padding =。如果我们修剪填充=,将有11个字符。

这里一个疯狂的想法是,我们可以结合使用Unix Epoch和一个时期值的计数器来形成一个long值。Unix纪元在C#DateTimeOffset.ToUnixEpochMilliseconds是在long格式,但第2个字节的8个字节的总为0,因为否则的日期时间值将是大于最大日期时间值。这样就给了我们2个字节来放置ushort计数器。

因此,总的来说,只要ID生成的数量不超过每毫秒65536,我们就可以拥有一个唯一的ID:

// This is the counter for current epoch. Counter should reset in next millisecond
ushort currentCounter = 123;

var epoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Because epoch is 64bit long, so we should have 8 bytes
var epochBytes = BitConverter.GetBytes(epoch);
if (BitConverter.IsLittleEndian)
{
    // Use big endian
    epochBytes = epochBytes.Reverse().ToArray();
}

// The first two bytes are always 0, because if not, the DateTime.UtcNow is greater 
// than DateTime.Max, which is not possible
var counterBytes = BitConverter.GetBytes(currentCounter);
if (BitConverter.IsLittleEndian)
{
    // Use big endian
    counterBytes = counterBytes.Reverse().ToArray();
}

// Copy counter bytes to the first 2 bytes of the epoch bytes
Array.Copy(counterBytes, 0, epochBytes, 0, 2);

// Encode the byte array and trim padding '='
// e.g. AAsBcTCCVlg
var shortUid = Convert.ToBase64String(epochBytes).TrimEnd('=');

1
    public static string ToTinyUuid(this Guid guid)
    {
        return Convert.ToBase64String(guid.ToByteArray())[0..^2]  // remove trailing == padding 
            .Replace('+', '-')                          // escape (for filepath)
            .Replace('/', '_');                         // escape (for filepath)
    }

用法

Guid.NewGuid().ToTinyUuid()

转换回来不是火箭科学,所以我会给你那么多。


0

如果您不需要键入字符串,则可以使用以下命令:

static class GuidConverter
{
    public static string GuidToString(Guid g)
    {
        var bytes = g.ToByteArray();
        var sb = new StringBuilder();
        for (var j = 0; j < bytes.Length; j++)
        {
            var c = BitConverter.ToChar(bytes, j);
            sb.Append(c);
            j++;
        }
        return sb.ToString();
    }

    public static Guid StringToGuid(string s) 
        => new Guid(s.SelectMany(BitConverter.GetBytes).ToArray());
}

这会将Guid转换为8个字符的字符串,如下所示:

{b77a49a5-182b-42fa-83a9-824ebd6ab58d}->“䦥띺ᠫ䋺ꦃ乱檽趵”

{c5f8f7f5-8a7c-4511-b667-8ad36b446617}->“엸诙䔑架펊䑫ᝦ”


0

这是我生成随机且简短的唯一ID的小方法。使用加密rng进行安全的随机数生成。将所需的任何字符添加到chars字符串。

private string GenerateRandomId(int length)
{
    char[] stringChars = new char[length];
    byte[] randomBytes = new byte[length];
    using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
    {
        rng.GetBytes(randomBytes);
    }

    string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";           

    for (int i = 0; i < stringChars.Length; i++)
    {
        stringChars[i] = chars[randomBytes[i] % chars.Length];
    }

    return new string(stringChars);
}

0

不丢失字符(+ /-),并且如果您想在url中使用guid,则必须将其转换为base32

10000万无重复密钥

    public static List<string> guids = new List<string>();
    static void Main(string[] args)
    {
        for (int i = 0; i < 10000000; i++)
        {
            var guid = Guid.NewGuid();
            string encoded = BytesToBase32(guid.ToByteArray());
            guids.Add(encoded);
            Console.Write(".");
        }
        var result = guids.GroupBy(x => x)
                    .Where(group => group.Count() > 1)
                    .Select(group => group.Key);

        foreach (var res in result)
            Console.WriteLine($"Duplicate {res}");

        Console.WriteLine($"*********** end **************");
        Console.ReadLine();
    }

    public static string BytesToBase32(byte[] bytes)
    {
        const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        string output = "";
        for (int bitIndex = 0; bitIndex < bytes.Length * 8; bitIndex += 5)
        {
            int dualbyte = bytes[bitIndex / 8] << 8;
            if (bitIndex / 8 + 1 < bytes.Length)
                dualbyte |= bytes[bitIndex / 8 + 1];
            dualbyte = 0x1f & (dualbyte >> (16 - bitIndex % 8 - 5));
            output += alphabet[dualbyte];
        }

        return output;
    }


-1
private static readonly object _getUniqueIdLock = new object();
public static string GetUniqueId()
{       
    lock(_getUniqueIdLock)
    {
        System.Threading.Thread.Sleep(1);
        return DateTime.UtcNow.Ticks.ToString("X");
    }
}

1
虽然这是一个有趣的解决方案,但不能保证UtcNow每毫秒返回一个唯一的滴答值:根据这些备注,分辨率取决于系统计时器。此外,您最好确保系统时钟不会向后更改!(ur3an0的答案也有这些问题。)
Joe Sewell

同意 这是穷人的做法,不应在您自己良好控制的环境中使用。
user13971889

-2

您可以使用

code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);

6仅限其漂亮的字符599527143354

当用户简单地对其进行验证时

var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);

希望这对你有帮助


我始终保持密码简单,易记
工具包


-6

我使用Guid.NewGuid().ToString().Split('-')[0],它从数组中以“-”分隔的第一项开始。它足以代表一个唯一的密钥。


3
这是不正确的。请参阅此链接
Kasey Speakman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.