.NET Pengenal Unik Pendek


91

Saya membutuhkan pengenal unik di .NET (tidak dapat menggunakan GUID karena terlalu panjang untuk kasus ini).

Apakah orang-orang berpikir bahwa algoritme yang digunakan di sini adalah kandidat yang bagus atau Anda punya saran lain?


3
Seberapa pendek? Dan seberapa unik? GUID dijamin unik jika didasarkan pada alamat perangkat keras adaptor ethernet; apapun yang dihasilkan secara matematis murni tidak akan pernah bisa dibuktikan unik - hanya mungkin unik (dengan probabilitas astronomis tinggi).
Jon

Panjangnya 15, dan seunik (mungkin) mungkin
Noel

4
var acak = 4; // cukup baik
KristoferA

4
15 berapa panjangnya? 15 byte? Jika demikian, mengapa tidak menghapus satu byte dari guid ..?
KristoferA

3
@Kristofer menghapus satu byte secara astronomis meningkatkan peluang Anda untuk tabrakan utama. Jika Anda menghapus byte yang dipesan yang salah, itu mungkin memastikannya bertabrakan.
Chris Marisic

Jawaban:


100

Yang ini bagus - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx

dan juga di sini GUID seperti YouTube

Anda bisa menggunakan Base64:

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

Itu menghasilkan string seperti E1HKfn68Pkms5zsZsvKONw ==. Karena GUID selalu 128 bit, Anda dapat menghilangkan == yang Anda tahu akan selalu ada di akhir dan itu akan memberi Anda 22 karakter string. Ini tidak sesingkat YouTube.


11
Catatan singkat: jika ini diperlukan untuk URL, siapa pun yang menggunakan ini mungkin ingin membersihkan karakter '+' dan '/' juga
pootzko


1
Saya menginginkan id unik sepanjang maksimal 23 karakter untuk UnnyNet di Unity, dan saya terjebak dengan GUID saya yang sangat bodoh, dan Anda membuat saya sangat bahagia :)
nipunasudha

Menariknya (jika Anda bekerja dengan Zoom API), ini hampir pasti bagaimana mereka menghasilkan UUID mereka. Panjangnya 22 karakter dan dikodekan base64.
vgel

39

Saya menggunakan pendekatan yang mirip dengan Dor Cohen tetapi menghapus beberapa karakter khusus:

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

Ini hanya akan menampilkan karakter alfanumerik. UID tidak dijamin memiliki panjang yang selalu sama. Berikut adalah contoh yang dijalankan:

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg

6
Anda akan kehilangan beberapa properti yang dijamin oleh GUID dengan membuang informasi seperti ini. Saya akan merekomendasikan untuk mengganti karakter yang Anda tidak nyaman dengan karakter yang berbeda sambil mempertahankan bijection antara GUID dan format serialisasi mereka.
Lukáš Lánský

3
Ini bagus untuk digunakan jika Anda TIDAK ingin mengonversi kembali ke GUID tetapi hanya perlu karakter acak untuk sesuatu yang lain .. misalnya melacak pekerja di multi utas, atau Awalan Logging untuk objek berulir, dll.
Piotr Kula

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

Pertahankan tanggal dasar (dalam hal ini adalah 1 Jan 2016) sejak Anda mulai membuat ID ini. Ini akan membuat id Anda lebih kecil.

Nomor yang Dihasilkan: 3af3c14996e54


millisecondsselalu 0 untuk DateTimeobjek itu
Teejay

Hapus juga kalimat terakhir.
Teejay

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

4
Tidak baik untuk pembuatan id hampir pada saat yang sama seperti di for-loop.eg dotnetfiddle.net/L3MIgZ
Jaider

17

Paket sederhana yang dapat digunakan. Saya menggunakannya untuk generator id permintaan temporal.

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

https://github.com/bolorundurowb/shortid

Kegunaan System.Random

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

(dari halaman github)

Jika Anda ingin mengontrol tipe id yang dihasilkan dengan menentukan apakah Anda menginginkan angka, karakter khusus dan panjangnya, panggil metode Generate dan berikan tiga parameter, yang pertama adalah boolean yang menyatakan apakah Anda menginginkan angka, yang kedua adalah boolean yang menyatakan apakah Anda mau karakter khusus, angka terakhir yang menunjukkan preferensi panjang Anda.

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

10

Sejauh yang saya tahu, hanya melepas sebagian GUID tidak dijamin unik - pada kenyataannya, itu jauh dari unik.

Hal terpendek yang saya tahu yang menjamin keunikan global ditampilkan dalam posting blog ini oleh Jeff Atwood . Dalam posting tertaut, dia membahas berbagai cara untuk mempersingkat GUID, dan pada akhirnya menurunkannya menjadi 20 byte melalui pengkodean Ascii85 .

Namun, jika Anda benar-benar membutuhkan solusi yang tidak lebih dari 15 byte, saya khawatir Anda tidak punya pilihan lain selain menggunakan sesuatu yang tidak dijamin unik secara global.


6

Nilai IDENTITAS harus unik dalam database, tetapi Anda harus menyadari keterbatasannya ... misalnya, ini membuat penyisipan data massal pada dasarnya tidak mungkin yang akan memperlambat Anda jika Anda bekerja dengan record dalam jumlah yang sangat besar.

Anda juga mungkin dapat menggunakan nilai tanggal / waktu. Saya telah melihat beberapa database di mana mereka menggunakan tanggal / waktu untuk menjadi PK, dan meskipun tidak super bersih - itu berhasil. Jika Anda mengontrol sisipan, Anda dapat secara efektif menjamin bahwa nilainya akan unik dalam kode.


6

Untuk aplikasi lokal saya, saya menggunakan pendekatan berbasis waktu ini:

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

di sini solusi saya, tidak aman untuk konkurensi, tidak lebih dari 1000 GUID per detik dan aman utas.

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

kode tidak dioptimalkan, cukup sampel !.


Meskipun merupakan solusi yang menarik, tidak ada jaminan bahwa UtcNowmengembalikan nilai centang unik untuk setiap milidetik: menurut komentar , resolusinya bergantung pada timer sistem. Selain itu, Anda sebaiknya memastikan jam sistem tidak berubah mundur! (Karena jawaban pengguna13971889 menabrak pertanyaan ini ke bagian atas umpan saya, dan saya mengkritik jawaban itu, saya pikir saya harus mengulangi kritik itu di sini.)
Joe Sewell

3

Jika aplikasi Anda tidak memiliki beberapa JUTA orang, menggunakan yang menghasilkan string unik pendek di SAMA MILLISECOND, Anda dapat berpikir tentang menggunakan fungsi di bawah ini.

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

ubah yyyy menjadi yy jika Anda hanya perlu menggunakan aplikasi Anda dalam 99 tahun ke depan.
Pembaruan 20160511 : Benar Fungsi Acak
- Tambahkan objek Kunci
- Pindahkan variabel acak dari fungsi RandomString
Ref


2
Ini bagus meskipun Anda tidak boleh menginisialisasi Random baru setiap kali - alasannya lockadalah untuk memungkinkan Anda menggunakan kembali Randominstance yang sama . Saya pikir Anda lupa menghapus baris itu!
NibblyPig

1

Saya tahu ini cukup jauh dari tanggal diposting ... :)

Saya memiliki generator yang hanya menghasilkan 9 karakter Hexa , misalnya: 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

Berdasarkan jawaban @ dorcohen dan komentar @pootzko. Anda bisa menggunakan ini. Aman di atas kabel.

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

Hasil jika ada yang bertanya-tanya: Jzhw2oVozkSNa2IkyK4ilA2atau coba sendiri di dotnetfiddle.net/VIrZ8j
chriszo111

1

Berdasarkan beberapa lainnya, berikut adalah solusi saya yang menyediakan panduan yang dikodekan berbeda yaitu URL (dan Docker) aman dan tidak kehilangan informasi apa pun:

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

Contoh keluarannya adalah:

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig

1

Dalam C # sebuah longnilai memiliki 64 bit, yang jika dikodekan dengan Base64 akan ada 12 karakter, termasuk 1 padding =. Jika padding kita rapikan =, akan ada 11 karakter.

Satu ide gila di sini adalah kita bisa menggunakan kombinasi Unix Epoch dan penghitung untuk satu nilai epoch untuk membentuk longnilai. Unix Epoch dalam C # DateTimeOffset.ToUnixEpochMillisecondsdalam longformat, tetapi 2 byte pertama dari 8 byte selalu 0, karena jika tidak, nilai waktu tanggal akan lebih besar dari nilai waktu tanggal maksimum. Jadi itu memberi kita 2 byte untuk menempatkan ushortpenghitung.

Jadi, secara total, selama jumlah pembuatan ID tidak melebihi 65536 per milidetik, kita dapat memiliki ID unik:

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

Pemakaian

Guid.NewGuid().ToTinyUuid()

Bukan ilmu roket untuk mengubahnya kembali, jadi saya akan meninggalkan Anda sebanyak itu.


0

Jika Anda tidak perlu mengetikkan string, Anda dapat menggunakan yang berikut ini:

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

Ini akan mengubah Panduan menjadi String 8 karakter seperti ini:

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

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


0

Inilah metode kecil saya untuk menghasilkan id unik acak dan pendek. Menggunakan rng kriptografi untuk pembuatan nomor acak yang aman. Tambahkan karakter apa pun yang Anda butuhkan ke charsstring.

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

untuk tidak kehilangan karakter (+ / -) dan jika Anda ingin menggunakan guid Anda di url, itu harus diubah menjadi base32

untuk 10.000.000 tidak ada kunci duplikat

    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
Meskipun merupakan solusi yang menarik, tidak ada jaminan bahwa UtcNowmengembalikan nilai centang unik untuk setiap milidetik: menurut komentar , resolusinya bergantung pada timer sistem. Selain itu, Anda sebaiknya memastikan jam sistem tidak berubah mundur! (Jawaban ur3an0 juga memiliki masalah ini.)
Joe Sewell

Sepakat. Ini adalah pendekatan orang miskin dan tidak boleh digunakan di luar lingkungan Anda yang terkontrol dengan baik.
pengguna13971889

-2

kamu bisa memakai

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

nya 6karakter bagus saja, 599527,143354

dan saat pengguna melakukannya dengan mudah

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

semoga ini membantu Anda


Saya selalu menjaga kata sandi saya sederhana, mudah diingat
Toolkit

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.