.NET Short Unique Identifier


90

Ich benötige eine eindeutige Kennung in .NET (kann die GUID nicht verwenden, da sie für diesen Fall zu lang ist).

Denken die Leute, dass der hier verwendete Algorithmus ein guter Kandidat ist, oder haben Sie andere Vorschläge?


3
Wie kurz? Und wie einzigartig? Die GUID ist garantiert eindeutig, wenn sie auf der Hardwareadresse eines Ethernet-Adapters basiert. Alles, was rein mathematisch produziert wird, kann niemals nachweislich einzigartig sein - nur wahrscheinlich einzigartig (mit einer astronomisch hohen Wahrscheinlichkeit).
Jon

15 in der Länge und so einzigartig (wahrscheinlich) wie möglich
Noel

3
var random = 4; // gut genug
KristoferA

3
15 was in der Länge? 15 Bytes? Wenn ja, warum nicht einfach ein Byte von einer Guid entfernen?
KristoferA

3
@KristoferEin astronomisches Entfernen eines Bytes in einem Guid erhöht astronomisch die Wahrscheinlichkeit von Schlüsselkollisionen. Wenn Sie das falsch geordnete Byte entfernen, kann dies zu einer Kollision führen.
Chris Marisic

Antworten:


96

Dies ist eine gute - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx

und auch hier YouTube-ähnliche GUID

Sie könnten Base64 verwenden:

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

Dadurch wird eine Zeichenfolge wie E1HKfn68Pkms5zsZsvKONw == generiert. Da eine GUID immer 128 Bit umfasst, können Sie das == weglassen, von dem Sie wissen, dass es immer am Ende vorhanden ist und das Ihnen eine 22-stellige Zeichenfolge gibt. Dies ist jedoch nicht so kurz wie YouTube.


11
Ein kurzer Hinweis: Wenn dies für URLs benötigt wird, möchte jeder, der dies verwendet, möglicherweise auch die Zeichen '+' und '/'
bereinigen


1
Ich wollte eine eindeutige ID mit maximal 23 Zeichen für UnnyNet in Unity, und ich war mit meinen großen dummen GUIDs festgefahren, und du hast mich so glücklich gemacht :)
nipunasudha

37

Ich benutze einen ähnlichen Ansatz wie Dor Cohens, entferne aber einige Sonderzeichen:

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

Dies gibt nur alphanumerische Zeichen aus. Es ist nicht garantiert, dass die UIDs immer dieselbe Länge haben. Hier ist ein Probelauf:

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg

6
Sie verlieren einige Eigenschaften, die durch GUIDs garantiert werden, indem Sie Informationen wie diese wegwerfen. Ich würde empfehlen, die Zeichen, mit denen Sie nicht vertraut sind, durch andere Zeichen zu ersetzen, während die Bijektion zwischen GUIDs und ihrem Serialisierungsformat erhalten bleibt.
Lukáš Lánský

2
Dies ist eine gute Option, wenn Sie NICHT zurück in eine GUID konvertieren möchten, sondern nur zufällige Zeichen für etwas anderes benötigen. Zum Beispiel das Verfolgen von Arbeitern in mehreren Threads oder das Protokollierungspräfix für Thread-Objekte usw.
Piotr Kula

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

Behalten Sie ein Basisdatum (in diesem Fall den 1. Januar 2016) bei, ab dem Sie mit der Generierung dieser IDs beginnen. Dadurch werden Ihre IDs kleiner.

Generierte Nummer: 3af3c14996e54


millisecondsist immer 0 für dieses DateTimeObjekt
Teejay

Entfernen Sie auch den letzten Satz.
Teejay

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

4
Nicht gut für die Generierung von IDs fast gleichzeitig wie in einem for-loop.eg dotnetfiddle.net/L3MIgZ
Jaider

16

Einfaches nutzbares Paket. Ich benutze es für den zeitlichen Anforderungs-ID-Generator.

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

https://github.com/bolorundurowb/shortid

Verwendet System.Random

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

(von der Github-Seite)

Wenn Sie den Typ der generierten ID steuern möchten, indem Sie angeben, ob Sie Zahlen, Sonderzeichen und die Länge möchten, rufen Sie die Generate-Methode auf und übergeben Sie drei Parameter. Der erste ist ein Boolescher Wert, der angibt, ob Sie Zahlen möchten, der zweite ein Boolescher Wert, der angibt, ob Sie möchten Sonderzeichen, die letzte eine Zahl, die Ihre Längenpräferenz angibt.

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

10

Soweit ich weiß, ist es nicht garantiert einzigartig , nur einen Teil einer GUID zu entfernen - tatsächlich ist es alles andere als einzigartig.

Das kürzeste, was ich weiß, das globale Einzigartigkeit garantiert, wird in diesem Blog-Beitrag von Jeff Atwood vorgestellt . In dem verlinkten Beitrag beschreibt er mehrere Möglichkeiten, eine GUID zu verkürzen und sie am Ende über Ascii85-Codierung auf 20 Byte zu reduzieren .

Wenn Sie jedoch unbedingt eine Lösung benötigen, die nicht länger als 15 Byte ist, haben Sie leider keine andere Wahl, als etwas zu verwenden, von dem nicht garantiert wird, dass es global einzigartig ist.


6

IDENTITY-Werte sollten in einer Datenbank eindeutig sein, aber Sie sollten sich der Einschränkungen bewusst sein. Dies macht beispielsweise das Einfügen von Massendaten grundsätzlich unmöglich, was Sie verlangsamt, wenn Sie mit einer sehr großen Anzahl von Datensätzen arbeiten.

Möglicherweise können Sie auch einen Datums- / Zeitwert verwenden. Ich habe mehrere Datenbanken gesehen, in denen Datum und Uhrzeit als PK verwendet werden, und obwohl es nicht besonders sauber ist, funktioniert es. Wenn Sie die Einfügungen steuern, können Sie effektiv sicherstellen, dass die Werte im Code eindeutig sind.


6

Für meine lokale App verwende ich diesen zeitbasierten Ansatz:

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

Hier ist meine Lösung nicht sicher für Parallelität, nicht mehr von 1000 GUIDs pro Sekunde und threadsicher.

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

Code nicht optimiert, nur Probe!.


Obwohl dies eine interessante Lösung ist, gibt es keine Garantie dafür, dass UtcNowfür jede Millisekunde ein eindeutiger Tick-Wert zurückgegeben wird: Gemäß den Anmerkungen hängt die Auflösung vom System-Timer ab. Außerdem sollten Sie sicherstellen, dass sich die Systemuhr nicht rückwärts ändert! (Da die Antwort von user13971889 diese Frage an die Spitze meines Feeds gestoßen hat und ich diese Antwort kritisiert habe, sollte ich diese Kritik hier wiederholen.)
Joe Sewell

3

Wenn Ihre App nicht über ein paar MILLIIONEN Personen verfügt, die in der GLEICHEN MILLISECOND eine kurze, eindeutige Zeichenfolge generieren, können Sie über die Verwendung der folgenden Funktion nachdenken.

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

Ändern Sie JJJJ in JJ, wenn Sie Ihre App in den nächsten 99 Jahren nur noch verwenden müssen.
Update 20160511 : Richtig Zufallsfunktion
- Hinzufügen Objekt sperren
- Move Zufallsvariable aus Randomfunktion
Ref


1
Dies ist großartig, obwohl Sie nicht jedes Mal einen neuen Zufall initialisieren sollten. Der Grund dafür lockist, dass Sie dieselbe RandomInstanz wiederverwenden können . Ich denke, Sie haben vergessen, diese Zeile zu löschen!
NibblyPig

1

Ich weiß, dass es ziemlich weit vom veröffentlichten Datum entfernt ist ... :)

Ich habe einen Generator, der nur 9 Hexa-Zeichen erzeugt , zB: 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

Basierend auf der Antwort von @ dorcohen und dem Kommentar von @ pootzko. Sie können dies verwenden. Es ist sicher über den Draht.

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

Ergebnis, wenn sich jemand wundert: Jzhw2oVozkSNa2IkyK4ilA2oder versuchen Sie es unter dotnetfiddle.net/VIrZ8j
chriszo111

1

Basierend auf einigen anderen ist hier meine Lösung, die eine andere codierte Richtlinie bereitstellt, die URL- (und Docker-) sicher ist und keine Informationen verliert:

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

Beispielausgaben sind:

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig

1

In C # hat ein longWert 64 Bit. Wenn er mit Base64 codiert wird, gibt es 12 Zeichen, einschließlich 1 Auffüllung =. Wenn wir die Polsterung kürzen =, gibt es 11 Zeichen.

Eine verrückte Idee hier ist, dass wir eine Kombination aus Unix-Epoche und einem Zähler für einen Epochenwert verwenden könnten, um einen Wert zu bilden long. Die Unix-Epoche in C # DateTimeOffset.ToUnixEpochMillisecondshat das longFormat, aber die ersten 2 Bytes der 8 Bytes sind immer 0, da sonst der Datums- / Uhrzeitwert größer als der maximale Datums- / Uhrzeitwert ist. Das gibt uns also 2 Bytes, in die wir einen ushortZähler einfügen können.

Insgesamt können wir also eine eindeutige ID haben, solange die Anzahl der ID-Generierungen 65536 pro Millisekunde nicht überschreitet:

// 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('=');

0

Wenn Sie die Zeichenfolge nicht eingeben müssen, können Sie Folgendes verwenden:

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

Dadurch wird die Guid in eine 8-stellige Zeichenfolge wie folgt konvertiert:

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

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


0

Hier ist meine kleine Methode, um eine zufällige und kurze eindeutige ID zu generieren. Verwendet ein kryptografisches Rng zur sicheren Erzeugung von Zufallszahlen. Fügen Sie der charsZeichenfolge die erforderlichen Zeichen hinzu .

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

Um keine Zeichen (+ / -) zu verlieren und wenn Sie Ihre Guid in einer URL verwenden möchten, muss sie in base32 umgewandelt werden

für 10 000 000 kein doppelter Schlüssel

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

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

Verwendung

Guid.NewGuid().ToTinyUuid()

Es ist keine Raketenwissenschaft, zurück zu konvertieren, also werde ich dich so sehr verlassen.



-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
Obwohl dies eine interessante Lösung ist, gibt es keine Garantie dafür, dass UtcNowfür jede Millisekunde ein eindeutiger Tick-Wert zurückgegeben wird: Gemäß den Anmerkungen hängt die Auflösung vom System-Timer ab. Außerdem sollten Sie sicherstellen, dass sich die Systemuhr nicht rückwärts ändert! (Die Antwort von ur3an0 hat auch diese Probleme.)
Joe Sewell

Einverstanden. Dies ist der Ansatz eines armen Mannes und sollte nicht außerhalb Ihrer eigenen gut kontrollierten Umgebung angewendet werden.
user13971889

-2

Sie können verwenden

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

seine 6nur schöne Zeichen, 599527,143354

und wenn Benutzer es einfach virifizieren

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

hoffe das hilft dir


Ich halte meine Passwörter immer einfach, leicht zu merken
Toolkit


-6

Ich benutze Guid.NewGuid().ToString().Split('-')[0], es bekommt das erste Element aus dem Array durch das '-' getrennt. Es ist genug, um einen eindeutigen Schlüssel darzustellen.


3
Das ist falsch. Siehe diesen Link .
Kasey Speakman
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.