Gibt es eine Methode zum Generieren einer UUID mit go-Sprache?

109

Ich habe Code, der so aussieht:

u := make([]byte, 16)
_, err := rand.Read(u)
if err != nil {
    return
}

u[8] = (u[8] | 0x80) & 0xBF // what does this do?
u[6] = (u[6] | 0x40) & 0x4F // what does this do?

return hex.EncodeToString(u)

Es gibt einen String mit einer Länge von 32 zurück, aber ich denke nicht, dass es eine gültige UUID ist. Wenn es eine echte UUID ist, warum ist es ein UUID, und was ist der Zweck des Codes, ändert sich der Wert von u[8]und u[6].

Gibt es eine bessere Möglichkeit, UUIDs zu generieren?

HardPass
quelle
1
Diese Antwort scheint jetzt passender.
ViKiG

Antworten:

32
u[8] = (u[8] | 0x80) & 0xBF // what's the purpose ?
u[6] = (u[6] | 0x40) & 0x4F // what's the purpose ?

Diese Zeilen klemmen die Werte von Byte 6 und 8 auf einen bestimmten Bereich. rand.ReadGibt zufällige Bytes im Bereich zurück 0-255, die nicht alle gültigen Werte für eine UUID sind. Soweit ich das beurteilen kann, sollte dies jedoch für alle Werte im Slice erfolgen.

Wenn Sie unter Linux arbeiten, können Sie alternativ anrufen /usr/bin/uuidgen.

package main

import (
    "fmt"
    "log"
    "os/exec"
)

func main() {
    out, err := exec.Command("uuidgen").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", out)
}

Welche Ausbeuten:

$ go run uuid.go 
dc9076e9-2fda-4019-bd2c-900a8284b9c4
jimt
quelle
23
Insbesondere ist dieser Ansatz langsam; Auf einem 2012 MacBook Air kann diese Strategie nur 170 UUIDs / Sekunde produzieren.
Jay Taylor
12
Mit der nu7hatch / gouuid-Bibliothek konnte ich 172.488 Uuids / Sekunde generieren.
Jay Taylor
2
Gute Erklärung der u[6]und u[8]Bytes.
Chowey
3
Auf meinem System (Ubuntu 15.10) musste ich auch die Befehlsausgabe über Strings ausführen. Trim (String (out)), um das Zeilenumbruchzeichen zu entfernen, andernfalls wurde es als nachfolgendes Zeichen eingegeben? Zeichen im Dateisystem.
Gregtczap
39
Das Aufrufen eines externen Programms, das möglicherweise existiert oder nicht, ist eine schreckliche Möglichkeit, diese ziemlich einfache Aufgabe zu erledigen.
Timmmm
96

Sie können UUIDs mithilfe der go-uuid- Bibliothek generieren . Dies kann installiert werden mit:

go get github.com/nu7hatch/gouuid

Sie können zufällige (Version 4) UUIDs generieren mit:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

Der zurückgegebene UUIDTyp ist ein 16-Byte-Array, sodass Sie den Binärwert einfach abrufen können. Über seine String()Methode wird auch die Standarddarstellung für Hex-Zeichenfolgen bereitgestellt.

Der Code, den Sie haben, sieht auch so aus, als würde er auch eine gültige UUID der Version 4 generieren: Die bitweise Manipulation, die Sie am Ende ausführen, setzt die Versions- und Variantenfelder der UUID, um sie korrekt als Version 4 zu identifizieren . Dies geschieht, um zufällige UUIDs von solchen zu unterscheiden, die über andere Algorithmen generiert wurden (z. B. UUIDs der Version 1 basierend auf Ihrer MAC-Adresse und Zeit).

James Henstridge
quelle
2
@Flimzy für Leute, die nicht wissen, was sie tun, das ist höchstwahrscheinlich wahr. Das Einführen unnötiger Abhängigkeiten ist immer eine schlechte Sache.
Erik Aigner
31
@ErikAigner Solange es 50 Zeilen sind, über die ich nicht nachdenken, schreiben und testen muss, nehme ich sie, danke. Ich habe andere Dinge zu tun, um das Rad neu zu erfinden.
RickyA
3
Diese Bibliothek scheint nicht RFC4122-konform zu sein: github.com/nu7hatch/gouuid/issues/28 (derzeit offene Ausgabe ab dem 01.02.2016)
Charles L.
1
@ErikAigner das Rad neu zu erfinden ist auch irgendwie unnötig. Wenn eine Bibliothek existiert und es gut macht, warum sollten Sie sich dann die Mühe machen, etwas anderes zu tun, als wenn Sie es tun, um zu lernen, wie es geht.
Sir
4
@ErikAigner Ich finde das einfach lächerlich. Niemand erfindet Dinge neu, die bereits erledigt wurden, es sei denn, Sie können es besser machen oder benötigen etwas Spezifisches für Ihr Programm. Wenn Sie den Code überprüfen und feststellen, dass er gut funktioniert, warum machen Sie es sich selbst? Sie verschwenden nicht nur Zeit und Kosten für die Entwicklung, sondern sind es auch Wenn Sie nicht genau wissen, was Sie tun, kann dies zu Fehlern oder einfach zu falschen Implementierungen führen. Diese Bibliotheken bestehen normalerweise aus Personen, die wissen, was sie tun. Es ist kein Anfänger, Bibliotheken von Drittanbietern zu verwenden, sondern nur ein Anfänger, der davon ausgeht, dass es funktioniert und den Code nicht zuerst überprüft.
Sir
70

Die go-uuidBibliothek ist NICHT RFC4122-kompatibel. Die Variantenbits sind nicht richtig gesetzt. Es gab mehrere Versuche von Community-Mitgliedern, dieses Problem zu beheben, aber Pull-Anforderungen für das Problem werden nicht akzeptiert.

Sie können UUIDs mithilfe der Go-Uuid-Bibliothek generieren, die ich basierend auf der go-uuidBibliothek neu geschrieben habe. Es gibt verschiedene Korrekturen und Verbesserungen. Dies kann installiert werden mit:

go get github.com/twinj/uuid

Sie können zufällige (Version 4) UUIDs generieren mit:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Der zurückgegebene UUID-Typ ist eine Schnittstelle und der zugrunde liegende Typ ist ein Array.

Die Bibliothek generiert auch v1-UUIDs und generiert korrekt v3- und 5-UUIDs. Es gibt verschiedene neue Methoden zum Drucken und Formatieren sowie neue allgemeine Methoden zum Erstellen von UUIDs basierend auf vorhandenen Daten.

twinj
quelle
4
Ich mag dieses Paket. Ich habe es offiziell für alle meine Anwendungen übernommen. Ich habe festgestellt, dass das nu7hatch-Paket nicht RFC4122-kompatibel ist.
Richard Eng
+1 Einverstanden, die Updates und Druck- / Formatierungserweiterungen sind bereits enthalten.
eduncan911
4
Haftungsausschluss fehlt? : p
Chakrit
3
Was ist die Bibliothek "unten"? Sie sollten die Verwendung von oben und unten bei SO vermeiden, da sich dies sehr schnell ändern kann.
Stephan Dollberg
Es gibt auch eine andere Entsprechung, satori / go.uuid . Ich habe es noch nicht ausprobiert, aber ich werde es als Ersatz für das tote Projekt
nu7hatch verwenden
51

"crypto / rand" ist ein plattformübergreifendes Paket für die Erzeugung zufälliger Bytes

package main

import (
    "crypto/rand"
    "fmt"
)

// Note - NOT RFC4122 compliant
func pseudo_uuid() (uuid string) {

    b := make([]byte, 16)
    _, err := rand.Read(b)
    if err != nil {
        fmt.Println("Error: ", err)
        return
    }

    uuid = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

    return
}
Ken Cloud
quelle
3
pseudo_uuidweil es die nicht zufälligen Bezeichner wie MAC-Adresse und was auch immer sonst RFC4122 angegeben fehlt? Also ist es eigentlich eher zufällig.
Xeoncross
2
gute Antwort; Ich habe es unter stackoverflow.com/a/48134820/1122270 erweitert , und ich denke, viele Leute müssen UUIDs nicht speziell verwenden (noch die sha1 / sha256, von denen ich dachte, dass ich sie für meinen eigenen Zufall verwenden muss). id problem), aber wollen einfach etwas zufälliges und einzigartiges, und Ihre Stichprobe bietet einen guten Start für eine Lösung
cnst
Vielen Dank! Einfach genug
Karl Pokus
1. Dies entspricht keinem Standard. 2. Wenn Sie nur %xProbleme mit Byte-Werten unter 128 haben, müssen Sie eine Auffüllung anwenden, dh %04xfür ein Byte-Paar
Ja͢ck
38

Es gibt eine offizielle Implementierung von Google: https://github.com/google/uuid

Das Generieren einer UUID der Version 4 funktioniert folgendermaßen:

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    id := uuid.New()
    fmt.Println(id.String())
}

Probieren Sie es hier aus: https://play.golang.org/p/6YPi1djUMj9

Shutefan
quelle
1
Der Godoc empfiehlt die Verwendung New()und es ist gleichbedeutend mituuid.Must(uuid.NewRandom())
Jim
@ Jim: Du hast recht! Ich habe meine Antwort entsprechend aktualisiert.
Shutefan
Beachten Sie, dass New () "tödlich" sein kann (was in einigen Fällen in Ordnung ist). In Fällen, in denen Ihr Programm nicht schwerwiegend sein soll, verwenden Sie einfach uuid.NewRandom () - das eine UUID und einen Fehler zurückgibt.
Tomer
@ Tomer: stimmt! Obwohl ich mich frage, unter welchen Umständen dies tatsächlich passieren würde. Dies ist der relevante Teil des Codes: github.com/google/uuid/blob/… Standardmäßig ist der Reader ein rand.Reader. Ich bin nicht sicher, ob dieser jemals einen Fehler zurückgeben würde oder ob dies nur mit einem benutzerdefinierten Reader passieren kann ...
Shutefan
1
Hallo @shutefan - ich stimme zu, dass es selten sein kann. rand.Reader ruft Kernelfunktionen auf ( golang.org/src/crypto/rand/rand.go ). Diese können in bestimmten Szenarien fehlschlagen.
Tomer
12

Aus Russ Cox ' Beitrag :

Es gibt keine offizielle Bibliothek. Wenn Sie die Fehlerprüfung ignorieren, scheint dies gut zu funktionieren:

f, _ := os.Open("/dev/urandom")
b := make([]byte, 16)
f.Read(b)
f.Close()
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

Hinweis: In der ursprünglichen Version vor Go 1 lautete die erste Zeile:

f, _ := os.Open("/dev/urandom", os.O_RDONLY, 0)

Hier wird kompiliert und ausgeführt, es werden nur /dev/urandomalle Nullen auf dem Spielplatz zurückgegeben. Sollte vor Ort gut funktionieren.

Im selben Thread wurden einige andere Methoden / Referenzen / Pakete gefunden.

zzzz
quelle
12
Dies generiert jedoch keine gültige UUID: Für UUIDs der Version 4 (der Typ, der auf zufälligen Daten basiert) müssen einige Bits auf eine bestimmte Weise gesetzt werden, um Konflikte mit den nicht zufälligen UUID-Formaten zu vermeiden.
James Henstridge
4
import "crypto/rand"Meiner Meinung nach besser zu verwenden , aber +1 für uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). In Kombination mit dem OP-Code funktioniert dies hervorragend.
Chowey
2
Verwenden des Crypto / Rand-Pakets: play.golang.org/p/7JJDx4GL77 . Der Code von zzzz macht das, was crypt / rand macht, außer dass er auch Plattformen abdeckt, die / dev / urandom (Windows) nicht unterstützen.
Drew
Es sollte beachtet werden, dass dies plattformspezifisch ist
Dan Esparza
2
@Matt: Das Problem ist, dass die anderen UUID-Formate ihre Eindeutigkeit erhalten, indem sie an eine andere Behörde delegieren (z. B. dass Ihre Ethernet-MAC-Adresse eindeutig ist) und diese dann mit etwas anderem kombinieren (z. B. der Zeit plus einem Zähler). Wenn Sie eine zufällige UUID erstellen, die nicht korrekt als V4 formatiert ist, wird das System geschwächt.
James Henstridge
8

Wenn Sie als Teil der UUID-Spezifikation eine UUID aus einem Zufallsprinzip generieren, muss diese eine "4" als 13. Zeichen und eine "8", "9", "a" oder "b" im 17. ( Quelle ) enthalten.

// this makes sure that the 13th character is "4"
u[6] = (u[6] | 0x40) & 0x4F
// this makes sure that the 17th is "8", "9", "a", or "b"
u[8] = (u[8] | 0x80) & 0xBF 
Eric Chiang
quelle
4

Der Gorand Paket verfügt über eine UUID-Methode, die eine (zufällig generierte) UUID der Version 4 in ihrer kanonischen Zeichenfolgendarstellung ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") zurückgibt und RFC 4122-kompatibel ist.

Außerdem wird das Crypto / Rand-Paket verwendet, um die kryptografisch sicherste Generierung von UUIDs auf allen von Go unterstützten Plattformen sicherzustellen.

import "github.com/leonelquinteros/gorand"

func main() {
    uuid, err := gorand.UUID()
    if err != nil {
        panic(err.Error())
    }

    println(uuid)
} 
peiiion
quelle
4

Unter Linux können Sie lesen von /proc/sys/kernel/random/uuid:

package main

import "io/ioutil"
import "fmt"

func main() {
    u, _ := ioutil.ReadFile("/proc/sys/kernel/random/uuid")
    fmt.Println(string(u))
}

Keine externen Abhängigkeiten!

$ go run uuid.go 
3ee995e3-0c96-4e30-ac1e-f7f04fd03e44
Seelenschütteln
quelle
4
Downvoted, da die direkte Abhängigkeit von einer Host-Plattform in einer Programmiersprache, die für Anwendungen mit mehreren Plattformen verwendet wird, schlechter ist als eine externe Abhängigkeit.
Tschüss
1
Die Programmiersprache kann plattformübergreifend sein, aber es handelt sich um sehr verbreitete Linux-spezifische Lösungen, die niemals auf einer anderen Plattform verfügbar sein werden. Daher handelt es sich um eine gültige IMO-Antwort.
Tonne
1

Für Windows habe ich kürzlich Folgendes getan:

// +build windows

package main

import (
    "syscall"
    "unsafe"
)

var (
    modrpcrt4 = syscall.NewLazyDLL("rpcrt4.dll")
    procUuidCreate = modrpcrt4.NewProc("UuidCreate")
)

const (
    RPC_S_OK = 0
)

func NewUuid() ([]byte, error) {
    var uuid [16]byte
    rc, _, e := syscall.Syscall(procUuidCreate.Addr(), 1,
             uintptr(unsafe.Pointer(&uuid[0])), 0, 0)
    if int(rc) != RPC_S_OK {
        if e != 0 {
            return nil, error(e)
        } else {
            return nil, syscall.EINVAL
        }
    }
    return uuid[:], nil
}
kostix
quelle
2
Downvoted, da die direkte Abhängigkeit von einer Host-Plattform in einer Programmiersprache, die für Anwendungen mit mehreren Plattformen verwendet wird, schlechter ist als eine externe Abhängigkeit.
Tschüss
1
@Byebye, ich frage mich, warum Sie sich als Autorität betrachten, um zu entscheiden, was "schlimmer" ist (und was nicht), um alle Antworten auf diese Frage zu überfliegen und alle "systemabhängig" herunterzustimmen. Diese Antworten wurden gegeben, um a) den Horizont aller möglichen Entscheidungen zu erweitern und b) gemeinsam ein vollständiges Bild zu präsentieren. Also hören Sie bitte auf, kindisch "SO zu spielen" und überlegen Sie, bevor Sie handeln.
Kostix
Kurze Antwort. Verwaltbaren Code schreiben. Ihre Antwort kann nicht auf eine andere Plattform portiert werden. Wenn das OP seine Anwendung auf eine andere Plattform verschieben würde, würde die Anwendung unterbrochen. Ich hatte einen fairen Anteil an Leuten, die plattformabhängigen Code geschrieben haben, bei dem dies völlig unnötig ist und mehr Probleme verursacht, als es wert ist. Sie schreiben keinen Code nur für sich. Sie schreiben Code für die Personen, die ihn nach Ihrer Abwesenheit warten. Deshalb ist diese Antwort nicht angemessen. Kein Grund, auf Ad Hominems zurückzugreifen und mich kindisch zu nennen.
Tschüss
1
@Byebye, ich habe überreagiert, also bitte entschuldigen Sie mich für den Angriff. Ich bin immer noch nicht von Ihren Gründen überzeugt, aber angeblich ist es der Fall "Lasst uns zustimmen, nicht zuzustimmen".
Kostix
1

Diese Bibliothek ist unser Standard für die Generierung und Analyse von Benutzeroberflächen:

https://github.com/pborman/uuid

James McGill
quelle
Beachten Sie, dass Googles eigene Bibliothek ( github.com/google/uuid ) teilweise auf github.com/pborman/uuid basiert , das wiederum einige der von Google vorgenommenen Änderungen übernommen hat. Wenn Sie jedoch zu einem dieser Projekte beitragen möchten, müssen Sie angeblich einen Contributor License Agreement (CLA) unterzeichnen (oder unterzeichnet haben ). Dies war anscheinend im August 2015 nicht der Fall, als Ihre Antwort hinzugefügt wurde. @pborman hat das erst am 16. Februar 2016 hinzugefügt .
Gwyneth Llewelyn