.NET Mã định danh duy nhất ngắn gọn


91

Tôi cần một số nhận dạng duy nhất trong .NET (không thể sử dụng GUID vì nó quá dài cho trường hợp này).

Mọi người có nghĩ rằng thuật toán được sử dụng ở đây là một ứng cử viên tốt hay bạn có bất kỳ đề xuất nào khác không?


3
Ngắn như thế nào? Và độc đáo như thế nào? GUID được đảm bảo là duy nhất nếu dựa trên địa chỉ phần cứng của bộ điều hợp ethernet; bất cứ thứ gì được tạo ra thuần túy về mặt toán học không bao giờ có thể là duy nhất - chỉ có thể là duy nhất (với xác suất cao về mặt thiên văn).
Jon

Dài 15 và càng độc đáo (có thể) càng tốt
Noel

3
var ngẫu nhiên = 4; // đủ tốt
KristoferA

3
15 dài? 15 byte? Nếu vậy, tại sao không bỏ một byte ra khỏi một hướng dẫn ..?
KristoferA

3
@Kristofer Việc loại bỏ một byte trong một hướng dẫn làm tăng khả năng xảy ra va chạm chính của bạn. Nếu bạn loại bỏ byte được sắp xếp sai, nó có thể khiến nó chắc chắn bị va chạm.
Chris Marisic

Câu trả lời:


96

Cái này là một cái hay - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx

và cũng có ở đây GUID giống như YouTube

Bạn có thể sử dụng Base64:

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

Điều đó tạo ra một chuỗi như E1HKfn68Pkms5zsZsvKONw ==. Vì GUID luôn là 128 bit, bạn có thể bỏ qua dấu == mà bạn biết sẽ luôn xuất hiện ở cuối và điều đó sẽ cung cấp cho bạn một chuỗi 22 ký tự. Tuy nhiên, nó không ngắn như YouTube.


11
Một lưu ý ngắn: nếu điều này là cần thiết cho URL, bất cứ ai sử dụng này có thể muốn khử trùng '+' và '/' nhân vật cũng
pootzko


1
Tôi muốn có một tối đa 23 char id dài duy nhất cho UnnyNet tại Unity, và tôi đã bị mắc kẹt với GUID câm lớn của tôi, và bạn khiến tôi rất hạnh phúc :)
nipunasudha

38

Tôi sử dụng cách tiếp cận tương tự như của Dor Cohen nhưng loại bỏ một số ký tự đặc biệt:

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

Điều này sẽ chỉ xuất ra các ký tự chữ và số. Các UID không được đảm bảo luôn có cùng độ dài. Đây là một cuộc chạy mẫu:

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg

6
Bạn sẽ mất một số thuộc tính được GUIDs đảm bảo bằng cách vứt bỏ thông tin như thế này. Tôi khuyên bạn nên thay thế các ký tự mà bạn không hài lòng bằng các ký tự khác nhau trong khi vẫn bảo toàn phân tích giữa các GUID và định dạng tuần tự hóa của chúng.
Lukáš Lánský

3
Đây là một cái hay để sử dụng nếu bạn KHÔNG muốn chuyển đổi lại thành GUID nhưng chỉ cần các ký tự ngẫu nhiên cho thứ gì đó khác .. ví dụ: theo dõi công nhân trong nhiều luồng hoặc Tiền tố ghi nhật ký cho các đối tượng luồng, v.v.
Piotr Kula

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

Giữ một ngày cơ sở (trong trường hợp này là ngày 1 tháng 1 năm 2016) từ khi bạn bắt đầu tạo các id này. Điều này sẽ làm cho id của bạn nhỏ hơn.

Số đã tạo: 3af3c14996e54


millisecondsluôn là 0 cho DateTimeđối tượng đó
Teejay

Đồng thời bỏ câu cuối cùng.
Teejay

1
oneliner: var uniqueId = (DateTime.Now.Ticks - new DateTime (2016, 1, 1) .Ticks) .ToString ("x");
Sgedda

4
Không tốt cho thế hệ của id gần như cùng lúc như thế nào trong một for-loop.eg dotnetfiddle.net/L3MIgZ
Jaider

16

Gói sử dụng đơn giản. Tôi sử dụng nó cho trình tạo id yêu cầu tạm thời.

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

https://github.com/bolorundurowb/shortid

Sử dụng System.Random

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

(từ trang github)

Nếu bạn muốn kiểm soát loại id được tạo bằng cách chỉ định xem bạn có muốn số, ký tự đặc biệt và độ dài hay không, hãy gọi phương thức Tạo và chuyển ba tham số, tham số đầu tiên là boolean cho biết bạn có muốn số hay không, tham số thứ hai là boolean cho biết bạn có muốn ký tự đặc biệt, số cuối cùng cho biết tùy chọn độ dài của bạn.

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

10

Theo như tôi biết, chỉ cần loại bỏ một phần của GUID không được đảm bảo là duy nhất - trên thực tế, nó còn lâu mới là duy nhất.

Điều ngắn gọn nhất mà tôi biết đảm bảo tính độc nhất toàn cầu được nêu trong bài đăng trên blog này của Jeff Atwood . Trong bài đăng được liên kết, anh ấy thảo luận về nhiều cách để rút ngắn GUID và cuối cùng, nó giảm xuống còn 20 byte thông qua mã hóa Ascii85 .

Tuy nhiên, nếu bạn thực sự cần một giải pháp không dài hơn 15 byte, tôi e rằng bạn không có lựa chọn nào khác ngoài việc sử dụng thứ gì đó không được đảm bảo là duy nhất trên toàn cầu.


6

Giá trị IDENTITY phải là duy nhất trong cơ sở dữ liệu, nhưng bạn nên biết các hạn chế ... ví dụ, nó khiến việc chèn dữ liệu hàng loạt về cơ bản là không thể, điều này sẽ làm chậm bạn nếu bạn đang làm việc với một số lượng rất lớn bản ghi.

Bạn cũng có thể sử dụng giá trị ngày / giờ. Tôi đã thấy một số cơ sở dữ liệu nơi họ sử dụng ngày / giờ để làm PK, và mặc dù nó không quá sạch nhưng nó hoạt động. Nếu bạn kiểm soát các phần chèn, bạn có thể đảm bảo hiệu quả rằng các giá trị sẽ là duy nhất trong mã.


6

Đối với ứng dụng địa phương của tôi, tôi đang sử dụng phương pháp dựa trên thời gian này:

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

đây là giải pháp của tôi, không an toàn cho đồng thời, không quá 1000 GUID mỗi giây và an toàn cho chuỗi.

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

mã không được tối ưu hóa, chỉ là mẫu !.


Mặc dù là một giải pháp thú vị, nhưng không có gì đảm bảo UtcNowtrả về giá trị đánh dấu duy nhất cho mỗi mili giây: theo nhận xét , độ phân giải phụ thuộc vào bộ đếm thời gian hệ thống. Ngoài ra, bạn nên đảm bảo rằng đồng hồ hệ thống không thay đổi ngược lại! (Vì câu trả lời của người dùng13971889 đưa câu hỏi này lên đầu nguồn cấp dữ liệu của tôi và tôi đã phê bình câu trả lời đó, tôi nghĩ mình nên lặp lại lời chỉ trích đó ở đây.)
Joe Sewell

3

Nếu ứng dụng của bạn không có vài TRIỆU người, bằng cách sử dụng tạo chuỗi ngắn duy nhất với mức CÙNG TRIỆU GIÂY, bạn có thể nghĩ đến việc sử dụng hàm bên dưới.

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());
}

đổi yyyy thành yy nếu bạn chỉ cần sử dụng ứng dụng của mình trong 99 năm tới.
Cập nhật 20160511 : Đúng chức năng Ngẫu nhiên
- Thêm đối tượng Khóa
- Di chuyển biến ngẫu nhiên ra khỏi hàm RandomString
Tham khảo


1
Điều này thật tuyệt mặc dù bạn không nên khởi tạo một Random mới mọi lúc - lý do locklà để cho phép bạn sử dụng lại cùng một Randomphiên bản. Tôi nghĩ bạn đã quên xóa dòng đó!
NibblyPig

1

Tôi biết nó còn khá xa so với ngày đăng ... :)

Tôi có một máy phát điện chỉ sản xuất 9 ký tự Hexa , ví dụ: 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

Dựa trên câu trả lời của @ dorcohen và bình luận của @ pootzko. Bạn có thể sử dụng cái này. Nó là an toàn qua dây.

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

Kết quả nếu kỳ ai: Jzhw2oVozkSNa2IkyK4ilA2hoặc thử mình ở dotnetfiddle.net/VIrZ8j
chriszo111

1

Dựa trên một số giải pháp khác, đây là giải pháp của tôi cung cấp một hướng dẫn được mã hóa khác là URL (và Docker) an toàn và không làm mất bất kỳ thông tin nào:

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

Kết quả đầu ra ví dụ là:

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig

1

Trong C #, một longgiá trị có 64 bit, nếu mã hóa bằng Base64 sẽ có 12 ký tự, trong đó có 1 phần đệm =. Nếu chúng ta cắt bỏ phần đệm =, sẽ có 11 ký tự.

Một ý tưởng điên rồ ở đây là chúng ta có thể sử dụng kết hợp Unix Epoch và bộ đếm cho một giá trị kỷ nguyên để tạo thành một longgiá trị. Unix Epoch trong C # DateTimeOffset.ToUnixEpochMillisecondslongđịnh dạng, nhưng 2 byte đầu tiên trong số 8 byte luôn là 0, vì nếu không giá trị ngày giờ sẽ lớn hơn giá trị thời gian ngày tối đa. Vì vậy, điều đó cho chúng ta 2 byte để đặt một bộ ushortđếm vào.

Vì vậy, tổng cộng, miễn là số lần tạo ID không vượt quá 65536 mỗi mili giây, chúng tôi có thể có một ID duy nhất:

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

Sử dụng

Guid.NewGuid().ToTinyUuid()

Nó không phải là khoa học tên lửa để chuyển đổi trở lại, vì vậy tôi sẽ để lại cho bạn nhiều như vậy.


0

Nếu bạn không cần nhập chuỗi, bạn có thể sử dụng như sau:

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());
}

Điều này sẽ chuyển hướng dẫn thành một chuỗi 8 ký tự như sau:

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

{c5f8f7f5-8a7c-4511-b667-8ad36b446617} -> " 엸 詼 䔑 架 펊 䑫 ᝦ"


0

Đây là phương pháp nhỏ của tôi để tạo một id duy nhất ngẫu nhiên và ngắn gọn. Sử dụng rng mật mã để tạo số ngẫu nhiên an toàn. Thêm bất kỳ ký tự nào bạn cần vào charschuỗi.

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

để không bị mất các ký tự (+ / -) và nếu bạn muốn sử dụng hướng dẫn của mình trong một url, nó phải được chuyển đổi thành base32

với giá 10 000 000 không có khóa trùng lặp

    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
Mặc dù là một giải pháp thú vị, nhưng không có gì đảm bảo UtcNowtrả về giá trị đánh dấu duy nhất cho mỗi mili giây: theo nhận xét , độ phân giải phụ thuộc vào bộ đếm thời gian hệ thống. Ngoài ra, bạn nên đảm bảo rằng đồng hồ hệ thống không thay đổi ngược lại! (Câu trả lời của ur3an0 cũng có những vấn đề này.)
Joe Sewell

Đã đồng ý. Đây là cách tiếp cận của một người nghèo và không nên được sử dụng ngoài môi trường được kiểm soát tốt của chính bạn.
user13971889

-2

bạn có thể dùng

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

6chỉ những ký tự đẹp của nó 599527,143354

và khi người dùng xác minh nó đơn giản

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

hy vọng điều này sẽ giúp bạn


Tôi luôn luôn giữ mật khẩu của tôi đơn giản, dễ nhớ
Toolkit


-6

Tôi sử dụng Guid.NewGuid().ToString().Split('-')[0], nó nhận mục đầu tiên từ mảng được phân tách bằng dấu '-'. Nó đủ để đại diện cho một khóa duy nhất.


3
Điều này là không đúng. Xem liên kết này .
Kasey Speakman,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.