5387 Stimmen

Wie man eine GUID / UUID erstellt

Ich versuche, in JavaScript global eindeutige Bezeichner zu erstellen. Ich bin mir nicht sicher, welche Routinen in allen Browsern verfügbar sind, wie "zufällig" und gesetzt der eingebaute Zufallszahlengenerator ist, usw.

Die GUID / UUID sollte mindestens 32 Zeichen lang sein und im ASCII-Bereich bleiben, um Probleme bei der Weitergabe zu vermeiden.

38 Stimmen

GUIDs, die als Zeichenketten dargestellt werden, sind mindestens 36 und höchstens 38 Zeichen lang und entsprechen dem Muster ^\{?[a-zA-Z0-9]{36}?\}$ und sind daher immer in ASCII.

5 Stimmen

David Bau bietet einen viel besseren Zufallszahlengenerator an, der unter davidbau.com/archives/2010/01/30/ Ich habe einen etwas anderen Ansatz zur Erzeugung von UUIDs unter blogs.cozi.com/tech/2010/04/generating-uuids-in-javascript.html

1 Stimmen

Seltsam, dass das noch niemand erwähnt hat, aber der Vollständigkeit halber: Es gibt eine Fülle von guid-Generatoren auf npm Ich wette, die meisten von ihnen funktionieren auch im Browser.

2voto

smallscript Punkte 559

Die folgenden uuid Umsetzung bietet eine verschiedene ES6 2020 Lösung mit BigInt und konzentriert sich auf "Use case intent for a uuid Entwurfsmuster"; insbesondere zur Verwendung mit indexedDb primaryKey Szenarien, in denen eine einheitliche zeitliche Abfolge und Zusammenstellung wertvoll sind.

Angesichts der Tatsache, dass auf diesen Beitrag über 30 Antworten eingegangen sind, geht es also los...

Dieser Beitrag hat:

  1. ein "TL;DR" code Abschnitt mit/ohne Selbstbeteiligung es6 class Xuid
  2. a Anwendungsfall y Motivationen Diskussion Abschnitt bezüglich der es6 class Xuid bereitgestellt Code .

TL;DR class Xuid Lösung für generische v4 uuid mit einer monotonen Uhr

Der folgende Code stammt aus der Smallscript-Datei EdgeS Web-Client-Bibliothek, die ich selbst geschrieben habe und die hier unter der freien MIT-Lizenz zur Verfügung gestellt wird. A GitHub-Version wird verfügbar sein, sobald das EdgeS Web-Client-Toolset freigegeben ist.

Beispiel für die Verwendung:

eval: console.log(Xuid.v4New)
emittiert: {1eb4a659-8bdc-4ce0-c002-b1d505d38ea8}

class Xuid {
  //@ edges.sm.st, ess.dev: MIT license Smallscript/David Simmons 2020
  //! Can't use `static const field = const` xbrowser (thus, const's duped)
  static get v4New() {
    const ns7Now = this.ns7Now, xnode48 = this.xnode48; let clock_seq13
    // monotonic `clock_seq` guarantee (13-bits/time-quantum)
    if(ns7Now <= this.ns7Now_prevSeq && this.ns7Now_prevSeq)
      clock_seq13 = ((this.ns7Now_prevSeq += 1n) - ns7Now) & 0b1_1111_1111_1111n
    else
      clock_seq13 = 0n, this.ns7Now_prevSeq = ns7Now
    const time60 = ((ns7Now << 4n) & 0xFFFF_FFFF_FFFF_0000n) |
                           (ns7Now & 0x0000_0000_0000_0FFFn),
              v4 = 0x1_00000000_0000_0000_0000_000000000000n |
      (time60 << 64n) | (0x00000000_0000_4000_0000_000000000000n) | // M: V4
      (0b110n << 61n) | (clock_seq13 << 48n) | // N: Variant-2 time-seq collation
      xnode48, s = v4.toString(16)//.substr(1)
    return `{${s.substr(1,8)}-${s.substr(9,4)}-${s.substr(13,4)}-${
      s.substr(17,4)}-${s.substr(21,12)}}`
  }
  static get xnode48()/*:<BigInt#48>*/{
    if(this.xnode48_) return this.xnode48_
    let clockSeqNode; if(typeof URL !== 'undefined' && URL.createObjectURL) {
      const url = URL.createObjectURL(new Blob())
      const id = (url.toString().split('/').reverse()[0]).split('-')
      URL.revokeObjectURL(url)
      clockSeqNode = BigInt('0x'+id[3]+id[4])
    }
    else {
      const a4 = this.a4; this.getRandomValues(this.a4);
      clockSeqNode = (BigInt(a4[2]) << 32n) | BigInt(a4[3])
    }
    // simulate the 48-bit node-id and 13-bit clock-seq
    // to combine with 3-bit uuid-variant
    return this.xnode48_ = clockSeqNode & 0xFFFF_FFFF_FFFFn;
  }
  static get jdNow()/*:<double#ns7>*/{
    // return 2440587.5+Date.now()/864e5 // <- Date-quantum-ms form (7ns form below)
    return this.jdFromNs7(this.ns7Now)
  }
  static get ns7Now()/*:<BigInt#60>*/{
    if(typeof performance !== 'undefined' && performance.now)
      Reflect.defineProperty(this, 'ns7Now',
        Reflect.getOwnPropertyDescriptor(this,'ns7Now_performance'))
    else
      Reflect.defineProperty(this, 'ns7Now',
        Reflect.getOwnPropertyDescriptor(this, 'ns7Now_Date'))
    return this.ns7Now
  }
  static get ns7Now_Date()/*:<BigInt#60>*/{
    // const epoch1582Ns7_bias = 0x1b2_1dd2_1381_4000  // V1 1582 Oct 15
    // const epoch1601Ns7_bias = 0x19d_b1de_d53e_8000n // FILETIME base
    const epoch1970Ns7 = BigInt(Date.now() * 1000_0.0)
    return epoch1970Ns7 + 0x1b2_1dd2_1381_4000n
  }
  static get ns7Now_performance()/*:<BigInt#60>*/{
    const epochPgNs7 = BigInt(performance.now()*/*15*/1000_0.0|/*17*/0)
    if(!this.epoch1970PgNs7) // performance.timing.navigationStart
      this.epoch1970PgNs7 = this.ns7Now_Date - epochPgNs7
    return epochPgNs7 + this.epoch1970PgNs7
  }
  static dateFromJd(jd) {return new Date((jd - 2440587.5) * 864e5)}
  static dateFromNs7(ns7) {
    return new Date(Number(ns7 - 0x1b2_1dd2_1381_4000n) / 1000_0.0)}
  static jdFromNs7(ns7) {   // atomic-clock leap-seconds (ignored)
    return 2440587.5 + (Number(ns7 - 0x1b2_1dd2_1381_4000n) / 864e9)
  }
  static ns7FromJd(jd) {
    return BigInt((jd - 2440587.5) * 864e9) + 0x1b2_1dd2_1381_4000n
  }
  static getRandomValues(va/*:<Uint32Array>*/) {
    if(typeof crypto !== 'undefined' && crypto.getRandomValues)
      crypto.getRandomValues(va)
    else for(let i = 0, n = va.length; i < n; i += 1)
      va[i] = Math.random() * 0x1_0000_0000 >>> 0
  }
  static get a4() {return this.a4_ || (this.a4_ = new Uint32Array(4))}
  static ntohl(v)/*:<BigInt>*/{
    let r = '0x', sign = 1n, s = BigInt(v).toString(16)
    if(s[0] == '-') s = s.substr(1), sign = -1n
    for(let i = s.length; i > 0; i -= 2)
      r += (i == 1) ? ('0' + s[i-1]) : s[i-2] + s[i-1]
    return sign*BigInt(r)
  }
  static ntohl32(v)/*:<Number>*/{return Number(this.ntohl(v))}
}

Motivation

Während v4 uuid definiert eine grundsätzlich zufällige uuid ist es wünschenswert, eine uuid Implementierung, die einige zusätzliche Merkmale unterstützen kann.

  • erstellt neue uuid Werte schnell und effizient (mit BigInt )

  • implementiert als eigenständiger Code mit einer nominal 80 loc lesbar class mit Kommentaren

  • enthält uuid Einzigartigkeit durch monotone time innerhalb einer context

  • stringifiziert, so dass die Zeichenkettenform:

    • kollabiert basierend auf time und dann context (mit uuid Variante-2)
    • in eine binäre Form zurückverwandelt, die die Daten korrekt identifiziert und wiederherstellt time
  • enthält JavaScript Taktgenauigkeit im Mikrosekundenbereich, sofern verfügbar

  • unterstützt umgebungsübergreifende Quanten von 100 Nanosekundeneinheiten auf der Grundlage des Julianischen Tages Epochenjahr 1582 Okt 15, V1-Kompatibilität. Auswahlmöglichkeiten, die ein einheitliches Zeit Zeitverhalten über ein Spektrum von Umgebungen und Anwendungsfällen hinweg im Einklang mit EdgeS y ESS Sprache Modell.

    Besonders geeignet für die Verwendung von Datenbanken mit Einrichtungen wie SQLite.

  • verwendet es6 class Design zur Vereinfachung der Erweiterbarkeit für nominelle Arbeiten zu erweitern um andere Funktionen zu ermöglichen uuid Varianten

  • für diese Buchung vereinheitlichte und integrierte grundlegende time y zugehörige eswc Bibliotheks-APIs.

    • Julianischer Tag API
    • ns7 (100-Nano-Sekunden-Quantum) API
    • ntohl API für die bequeme Neuordnung von Endian BigInt String-Darstellungen
  • abgeleitet von QKS Smalltalk 1991, AOS® [Agile Object System; Agents Object System] Technologie der Engine-Familie für Sprache, Framework und Laufzeiten bewahrt sie Kompatibilität mit einer Vielzahl aktueller und historischer Host-Betriebssysteme Modelle.

    • und zwar dort, wo die Xuid scalar string format mit geschweiften Klammern unterstützt guid , uuid y uid ( git , fossil , SqLite repo-id) Vertretungen, FILETIME など。

      wie in: {1eb4a659-8bdc-4ce0-c002-b1d505d38ea8}

  • und nicht zuletzt bietet es eine wünschenswerte Lösung für die Arbeit mit indexedDb object stores wobei die Verwendung eines uuid als die primaryKey begehrenswert wird.

    • Aktivierung von Auto-Sequencing-Funktionen
    • natürliche Sortierung der Zeichenketten
      • beachten Sie die subtil Verwendung von uuid Variante-2 umkehren time Wert des LHS in seinem stringiert Form.
    • natürlich und einfach put Aktualisierung von
    • natürliches Muster für efs (EdgeS virtuelles Dateisystem - automatische Namen)
    • service-worker y cloud-server Aktionen synchronisieren und replizieren

Zusammenfassung

Obwohl Kurz und bündig Ich hoffe, dass dies für den Moment eine ausreichende Erklärung ist; versuchen Sie es .

Und, bitte Sie können gerne Kommentare, Feedback oder Vorschläge einreichen.

Bei der Veröffentlichung als Teil des EdgeS Web-Client eswc Bibliothek auf GitHub die indexedDb Nutzungsmuster mit efs werden als Beispiele für ihre Design-Absichten, die Effizienz und Benutzerfreundlichkeit mit indexedDb y zugehörige PWA sync y replicate Szenarien.

Verwandte Seiten

Benchmarking uuid s/sec

const start = Xuid.ns7Now
for(let i = 100000; i; i -=1)
  Xuid.v4New
const end = Xuid.ns7Now
console.log(`Delta 7ns: ${(end-start)/100000n}`)

Daraus resultiert: Werte von 16..20 => ~2 Mikro-Sekunden => 500,000 uuid s/sec

2voto

Stephen Quan Punkte 16456

Für diejenigen, die JavaScript unter Windows verwenden (z. B., Windows-Skript-Host (WSH), CScript y HTA ). Man kann verwenden ActiveX . Genauer gesagt, die Scriptlet.Typelib Objekt:

WScript.Echo((new ActiveXObject("Scriptlet.TypeLib")).Guid)

Beachten Sie, dass diese Antwort nur für die von mir aufgeführten Technologien gilt. Sie funktioniert nicht in jedem Browser, auch nicht in Microsoft Kante ! Diese Antwort kann also von Fall zu Fall unterschiedlich ausfallen.

5 Stimmen

Ist ein solcher Ansatz im Jahr 2018 noch zeitgemäß? Wow :-)

2voto

Stefan Steiger Punkte 72861

Eine GUID oder UUID, wie sie in Nicht-Microsoft-Kreisen genannt wird, ist im Grunde nur eine kryptografische 128-Bit-Zufallszahl, wobei die UUID-Versionsnummer (1-5) in einem Byte an fester Stelle enthalten ist.

Wenn Sie also einfach eine Reihe von Zufallszahlen zwischen 0 und 65535 generieren und diese hexadezimal kodieren, etwa so:

function guid()
{
    function s4()
    {
        return Math.floor(Math.random() * 65536).toString(16).padStart(4, '0')
    } // End Function s4

    return s4() + s4() + '-' + s4() + '-' + "4" + s4().substr(1) + '-' + s4() + '-' + s4() + s4() + s4();
} // End Function guid

Sie erhalten eine gültige GUID, die jedoch aufgrund der Zufallsimplementierung nicht kryptografisch sicher ist.

Um eine kryptografisch sichere GUID zu erzeugen, müssen Sie window.crypto (oder window.msCrypto für Internet Explorer) verwenden.

Das geht so:

function cryptGuid()
{
    var array = new Uint16Array(8);
    (window.crypto || window.msCrypto).getRandomValues(array);
    var dataView = new DataView(array.buffer);

    var parts = [];

    for(var i = 0; i < array.length; ++i)
    {
        // 0&1,2,3,4,5-7 dataView.getUint16(0-7)
        if(i>1 && i<6) parts.push("-");
        parts.push(dataView.getUint16(i).toString(16).padStart(4, '0'));
    }

    parts[5] = "4" + parts[5].substr(1);
    // console.log(parts);
    return parts.join('').toUpperCase();// .toLowerCase();
}

cryptGuid();

Außerdem müssen Sie entscheiden, ob Sie die Zahl in Klein- oder Großbuchstaben zurückgeben wollen. Bestimmte Software erfordert Kleinbuchstaben (z. B. Reporting Service), während andere Großbuchstaben erzeugen (SQL Server).

2voto

tarkh Punkte 1813

UUID mit eingebautem Zeitstempel (Emitter/Parser)

Dies ist mein einfacher Ansatz zur Erzeugung einer gültigen UUID v4 mit sehr starker Eindeutigkeit und schneller Laufzeit.

Die Grundidee ist nicht neu, aber der Ansatz ist anders. Ich verwende einen Zeitstempel in Millisekunden aus dem date.now() (in der Node.js-Bibliothek, auf die ich später eingehen werde, verwende ich den Nanosekunden-Zeitstempel aus process.hrtime.bigint() ), und fügen Sie dann eine zufällige 5-stellige Zahl ( 10000-90000 ) an das Ende der Zeitstempelzeichenfolge.

Nach dem Zusammenführen der Zeichenketten bilde ich einfach eine gültige UUID aus Ziffern und einem Paar Sonderzeichen, so dass meine UUID nur aus Ziffern und einigen nicht numerischen Zeichen besteht. Bitte sehen Sie sich das unten an:

/*
 * uuid-timestamp (emitter)
 * UUID v4 based on timestamp
 *
 * Created by tarkh
 * tarkh.com (C) 2020
 */
const uuidEmit = () => {
  // Get now time
  const n = Date.now();
  // Generate random
  const r = Math.random();
  // Stringify now time and generate additional random number
  const s = String(n) + String(~~(r*9e4)+1e4);
  // Form UUID and return it
  return `${s.slice(0,8)}-${s.slice(8,12)}-4${s.slice(12,15)}-${[8,9,'a','b'][~~(r*3)]}${s.slice(15,18)}-${s.slice(s.length-12)}`;
};

// Generate 5 UUIDs
console.log(`${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}`);

Wenn man sich die Ergebnisse anschaut, sieht man, dass der erste Teil der UUIDs gleich ist, und dann kommt die Zufälligkeit. Das liegt daran, dass ich den Zeitstempel linear in die UUID eingefügt habe. Der Code erzeugt jede Millisekunde (Nanosekunde in der Node.js-Bibliothek) eine neue UUID und fügt am Ende eine zufällige 5-stellige Zahl hinzu, so dass die Kollisionswahrscheinlichkeit bei etwa 1 zu 10 Millionen pro Sekunde liegt. Wenn wir die Node.js-Bibliothek verwenden, steigt die sehr ungefähre Kollisionswahrscheinlichkeit auf 1 zu 10 Milliarden pro Sekunde.

In die UUID eingebauter Zeitstempel

Da wir einen Zeitstempel linear in die UUID einfügen, erhalten wir eine Funktion (gut oder schlecht - hängt von der Aufgabe ab) - die Fähigkeit, diesen Zeitstempel leicht aus der UUID zu extrahieren. Auf diese Weise können wir verstehen, wann die UUID veröffentlicht wurde:

/*
 * uuid-timestamp (parser)
 * UUID v4 based on timestamp
 *
 * Created by tarkh
 * tarkh.com (C) 2020
 */
const uuidParse = (uuid) => {
  // Get current timestamp string length
  let tl = String(Date.now()).length;
  // Strip out timestamp from UUID
  let ts = '';
  let i = -1;
  while(tl--) {
    i++;
    if(i===8||i===13||i===14||i===18||i===19||i===23) {
      tl++;
      continue;
    }
    ts += uuid[i];
  }
  return Number(ts);
};

// Get the timestamp when UUID was emitted
const time = uuidParse('15970688-7109-4530-8114-887109530114');

// Covert timestamp to date and print it
console.log(new Date(time).toUTCString());

Node.js

Die NPM-Version meines obigen Codes ist als Node.js-Modul . Diese Version ist sogar noch leistungsfähiger bei der Erzeugung eindeutiger Werte, da sie anstelle des Millisekunden-Zeitstempels den nanoseconds aus der Kombination von Systemzeit und process.hrtime.bigint() diff.

Benchmarks

Am Ende meines Beitrags möchte ich einige Leistungstests durchführen, die auf einigen der Antworten aus diesem Thema basieren. Natürlich ist meine Entscheidung nicht die schnellste, aber sie nimmt sicherlich die Spitzenplätze ein.

Prüfen Sie jsBench hier

0 Stimmen

Esto es technisch nicht eine konforme UUID4. Es ist nett, dass es den Zeitstempel hat, aber Sie sind besser dran, wenn Sie eine UUID1 an diesem Punkt verwenden. Bei diesem Schema besteht jedes Mal, wenn eine Kollision im Millisekundenbereich auftritt, eine Chance von 1 zu 10.000, was bei Ereignissen, die jede Sekunde gesendet werden, 1/1000 oder 1 zu 10 Millionen pro Sekunde bedeutet. Das ist so ziemlich garantiert, dass es in jeder Produktion, die größer als ein Hobby ist, auftritt. Semantisch gesehen, sollten UUIDs keine Zeitstempelfelder sein. Dafür sind Zeitstempelfelder ja da. Sie sind für "gib mir eine ID, die NIE kollidieren wird" gedacht.

1 Stimmen

@DeusXMachina danke für den Kommentar, ich stimme zu 100% zu. Das ist, warum in NodeJS Bibliothek, die ich am Ende meines Beitrags hingewiesen habe, ich verwenden process.hrtime.bigint() für Zeitstempel, was mir eine Nanosekunden-Skala (1 Million Chancen in einer Sekunde) + Zufallszahl aus 10000 a 90000 am Ende. So geht es schließlich zu Multi-Milliarden Chance in 1 Sekunde, wenn meine Berechnungen richtig ist. Wie auch immer, dies ist ein nicht standardisierter Ansatz mit einem speziellen Anwendungsfall, wie ich oben erwähnt habe.

1 Stimmen

Ah, das habe ich übersehen. Vielleicht bearbeiten Sie Ihre Erklärung, so dass es ein bisschen mehr offensichtlich, dass Sie ns Auflösung verwenden. "...erzeugt jede Millisekunde eine neue UUID + fügt eine zufällige 5-stellige Zahl hinzu". Warum nicht trotzdem uuid1 verwenden? Das bietet eine Auflösung von 0,1 µs plus weitere 48 Bits Entropie. de.wikipedia.org/wiki/

2voto

Mohan Ram Punkte 8045

Verwenden Sie nicht Math.random in jedem Fall, da es eine nicht kryptografische Quelle für Zufallszahlen erzeugt.

Die folgende Lösung mit crypto.getRandomValues

function uuidv4() {
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
    // tslint:disable-next-line: no-bitwise
    const r =
      (window.crypto.getRandomValues(new Uint32Array(1))[0] *
        Math.pow(2, -32) * 16) |
      0;
    // tslint:disable-next-line: no-bitwise
    const v = c === "x" ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

Dieser Link hilft Ihnen dabei, die unsichere Zufälligkeit geworfen von Scanner befestigen .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X