Existe-t-il une méthode pour générer un UUID avec go language

109

J'ai du code qui ressemble à ceci:

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)

Il renvoie une chaîne d'une longueur de 32, mais je ne pense pas que ce soit un UUID valide. S'il s'agit d'un UUID réel, pourquoi s'agit-il d'un UUID et quel est le but du code qui modifie la valeur de u[8]et u[6].

Existe-t-il un meilleur moyen de générer des UUID?

hardPass
la source
1
Cette réponse semble plus appropriée maintenant.
ViKiG

Réponses:

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

Ces lignes fixent les valeurs des octets 6 et 8 à une plage spécifique. rand.Readrenvoie des octets aléatoires dans la plage 0-255, qui ne sont pas toutes des valeurs valides pour un UUID. Pour autant que je sache, cela devrait être fait pour toutes les valeurs de la tranche.

Si vous êtes sous Linux, vous pouvez également appeler /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)
}

Ce qui donne:

$ go run uuid.go 
dc9076e9-2fda-4019-bd2c-900a8284b9c4
Jimt
la source
23
Notamment, cette approche est lente; sur un MacBook Air 2012, cette stratégie ne peut produire que 170 uuides / seconde.
Jay Taylor
12
Et en utilisant la bibliothèque nu7hatch / gouuid, j'ai pu générer 172 488 uuids / seconde.
Jay Taylor
2
Bonne explication des octets u[6]et u[8].
chowey
3
Sur mon système (Ubuntu 15.10), j'avais également besoin d'exécuter la sortie de la commande via des chaînes.Trim (string (out)) pour supprimer le caractère de nouvelle ligne, sinon il a été entré en tant que fin? caractère dans le système de fichiers.
gregtczap
39
Appeler un programme externe qui peut exister ou non est une manière terrible de faire cette tâche assez simple.
Timmmm
96

Vous pouvez générer des UUID à l'aide de la bibliothèque go-uuid . Cela peut être installé avec:

go get github.com/nu7hatch/gouuid

Vous pouvez générer des UUID aléatoires (version 4) avec:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

Le UUIDtype renvoyé est un tableau de 16 octets, vous pouvez donc récupérer facilement la valeur binaire. Il fournit également la représentation sous forme de chaîne hexadécimale standard via sa String()méthode.

Le code que vous avez semble également générer un UUID version 4 valide: la manipulation au niveau du bit que vous effectuez à la fin définit les champs de version et de variante de l'UUID pour l' identifier correctement comme version 4 . Ceci est fait pour distinguer les UUID aléatoires de ceux générés via d'autres algorithmes (par exemple, les UUID de la version 1 en fonction de votre adresse MAC et de l'heure).

James Henstridge
la source
2
@Flimzy pour les personnes qui ne savent pas ce qu'elles font, c'est probablement vrai. L'introduction de dépendances inutiles est toujours une mauvaise chose.
Erik Aigner
31
@ErikAigner Tant que c'est 50 lignes je n'ai pas à penser, écrire et tester, je vais les prendre merci .. J'ai autre chose à faire puis réinventer la roue.
RickyA
3
Cette bibliothèque semble ne pas être réellement conforme à la RFC4122: github.com/nu7hatch/gouuid/issues/28 (numéro actuellement ouvert au 2/1/2016)
Charles L.
1
@ErikAigner réinventer la roue est également un peu inutile. Si une bibliothèque existe et le fait bien, pourquoi s'embêter à faire la vôtre autrement que si vous le faites pour apprendre à le faire.
Sir
4
@ErikAigner, je trouve ça ridicule. Personne ne réinvente ce qui est déjà fait à moins que vous ne puissiez faire mieux ou que vous ayez besoin de quelque chose de spécifique à votre programme, si vous inspectez le code et voyez qu'il le fait bien, pourquoi vous embêter à le faire vous-même - non seulement vous perdez du temps et des coûts de développement, vous êtes aussi susceptibles d'apporter des bogues ou simplement de fausses implémentations si vous ne savez pas complètement ce que vous faites, ces bibliothèques sont généralement des personnes qui savent ce qu'elles font. Ce n'est pas un débutant d'utiliser des bibliothèques tierces, son seul débutant à supposer que cela fonctionne et à ne pas inspecter le code en premier.
Sir
70

La go-uuidbibliothèque n'est PAS conforme à la RFC4122. Les bits de variante ne sont pas définis correctement. Des membres de la communauté ont tenté à plusieurs reprises de corriger ce problème, mais les pull requests pour le correctif ne sont pas acceptées.

Vous pouvez générer des UUID à l'aide de la bibliothèque Go uuid que j'ai réécrite en fonction de la go-uuidbibliothèque. Il existe plusieurs correctifs et améliorations. Cela peut être installé avec:

go get github.com/twinj/uuid

Vous pouvez générer des UUID aléatoires (version 4) avec:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Le type UUID renvoyé est une interface et le type sous-jacent est un tableau.

La bibliothèque génère également des UUID v1 et génère correctement des UUID v3 et 5. Il existe plusieurs nouvelles méthodes pour faciliter l'impression et le formatage, ainsi que de nouvelles méthodes générales pour créer des UUID basés sur des données existantes.

twinj
la source
4
J'aime ce package. Je l'ai officiellement adopté pour toutes mes applications. J'ai trouvé que le package nu7hatch n'était pas conforme à la RFC4122.
Richard Eng
+1 D'accord, les mises à jour et les extensions d'impression / formatage sont déjà incluses.
eduncan911
4
Clause de non-responsabilité manquante? : p
chakrit
3
Quelle est la bibliothèque "ci-dessous"? Vous devriez éviter d'utiliser ci-dessus et ci-dessous sur SO car cela peut changer assez rapidement.
Stephan Dollberg
Il existe également un autre équivalent, satori / go.uuid . Je ne l'ai pas encore essayé mais je vais l'utiliser en remplacement du projet nu7hatch dead ...
shadyyx
52

"crypto / rand" est un paquet multiplateforme pour la génération aléatoire d'octets

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
la source
3
pseudo_uuidparce qu'il manque les identifiants non aléatoires comme l'adresse MAC et tout ce que RFC4122 spécifie? Donc c'est en fait plus aléatoire.
Xeoncross
2
bonne réponse; Je l'ai développé sur stackoverflow.com/a/48134820/1122270 , et je pense que beaucoup de gens n'ont pas besoin d'utiliser spécifiquement les UUID (ni le sha1 / sha256 que je pensais devoir utiliser pour mon propre aléatoire- id problème), mais vous voulez simplement quelque chose d'aléatoire et d'unique, et votre échantillon fournit un bon début pour une solution
cnst
Merci! Assez simple
Karl Pokus
1. Ceci n'est conforme à aucune norme 2. En utilisant juste %xdes problèmes avec des valeurs d'octets inférieures à 128, vous devez appliquer un remplissage, c'est- %04xà- dire pour une paire d'octets
Ja͢ck
38

Il existe une mise en œuvre officielle par Google: https://github.com/google/uuid

La génération d'un UUID version 4 fonctionne comme ceci:

package main

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

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

Essayez-le ici: https://play.golang.org/p/6YPi1djUMj9

shutefan
la source
1
Le godoc recommande d'utiliser New()et c'est équivalent àuuid.Must(uuid.NewRandom())
Jim
@Jim: vous avez raison! J'ai mis à jour ma réponse en conséquence.
shutefan
Notez que New () peut être "fatal" (ce qui est acceptable dans certains cas). Dans les cas où vous ne voulez pas que votre programme soit fatal, utilisez simplement uuid.NewRandom () - qui renvoie un UUID et une erreur.
Tomer
@Tomer: vrai! Bien que je me demande dans quelles circonstances cela se produirait réellement. Voici la partie pertinente du code: github.com/google/uuid/blob / ... Par défaut, le lecteur est un rand.Reader. Je ne sais pas si celui-ci renverrait jamais une erreur ou si cela ne peut se produire qu'avec un lecteur personnalisé ...
shutefan
1
Salut @shutefan - Je suis d'accord que cela peut être rare. rand.Reader appelle les fonctions du noyau ( golang.org/src/crypto/rand/rand.go ). Ceux-ci peuvent échouer dans certains scénarios.
Tomer
24

gofrs / uuid est le remplacement de satori / go.uuid , qui est le package UUID le plus étoilé pour Go . Il prend en charge les versions 1 à 5 d'UUID et est conforme aux normes RFC 4122 et DCE 1.1.

import "github.com/gofrs/uuid"

// Create a Version 4 UUID, panicking on error
u := uuid.Must(uuid.NewV4())
bmaupin
la source
12

D' après le message de Russ Cox :

Il n'y a pas de bibliothèque officielle. En ignorant la vérification des erreurs, cela semble fonctionner correctement:

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:])

Remarque: dans la version originale, antérieure à Go 1, la première ligne était:

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

Ici, il compile et exécute, ne /dev/urandomrenvoie que tous les zéros dans la cour de récréation. Devrait bien fonctionner localement.

Dans le même fil, il y a d'autres méthodes / références / packages trouvés.

zzzz
la source
12
Cependant, cela ne générera pas un UUID valide: les UUID de la version 4 (le type basé sur des données aléatoires) nécessitent que quelques bits soient définis d'une certaine manière pour éviter tout conflit avec les formats UUID non aléatoires.
James Henstridge
4
Mieux vaut utiliser import "crypto/rand"à mon avis, mais +1 pour uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). Combiné avec le code de l'OP, et cela fonctionne très bien.
chowey
2
En utilisant le package crypto / rand: play.golang.org/p/7JJDx4GL77 . Le code de zzzz fait ce que fait crypt / rand, sauf qu'il couvre également les plates-formes qui ne prennent pas en charge / dev / urandom (Windows).
Tiré
Il convient de noter que cela est spécifique à la plate-forme
Dan Esparza
2
@Matt: le problème est que les autres formats UUID obtiennent leur unicité en déléguant à une autre autorité (par exemple, que votre adresse MAC Ethernet est unique), puis en combinant cela avec autre chose (par exemple le temps plus un compteur). Si vous produisez un UUID aléatoire qui n'est pas correctement formaté en tant que V4, vous affaiblissez le système.
James Henstridge
8

Dans le cadre de la spécification uuid, si vous générez un uuid aléatoire, il doit contenir un "4" comme 13ème caractère et un "8", "9", "a" ou "b" dans le 17ème ( source ).

// 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
la source
4

Le package gorand a une méthode UUID qui renvoie un UUID version 4 (généré de manière aléatoire) dans sa représentation sous forme de chaîne canonique ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") et il est conforme à la RFC 4122.

Il utilise également le package crypto / rand pour garantir la génération d'UUID la plus sécurisée du point de vue cryptographique sur toutes les plates-formes prises en charge par Go.

import "github.com/leonelquinteros/gorand"

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

    println(uuid)
} 
peiiion
la source
4

Sous Linux, vous pouvez lire à partir de /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))
}

Pas de dépendances externes!

$ go run uuid.go 
3ee995e3-0c96-4e30-ac1e-f7f04fd03e44
secousse d'âme
la source
4
Évité parce que le fait de dépendre directement d'une plate-forme hôte dans un langage de programmation utilisé pour les applications multi-plates-formes est pire qu'une dépendance externe.
revoir
1
Le langage de programmation peut être multiplateforme, mais ce sont des solutions très courantes spécifiques à Linux qui ne le seront jamais sur une autre plate-forme, donc, c'est une réponse valide de l'OMI.
ton
1

Pour Windows, j'ai fait récemment ceci:

// +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
la source
2
Évité parce que le fait de dépendre directement d'une plate-forme hôte dans un langage de programmation utilisé pour les applications multi-plates-formes est pire qu'une dépendance externe.
revoir
1
@Byebye, je me demande pourquoi vous vous considérez comme une autorité pour décider de ce qui "est pire" (et de ce qui ne l'est pas) pour parcourir toutes les réponses données à cette question et voter contre toutes celles qui sont "dépendantes du système"? Ces réponses visaient a) à élargir l'horizon de tous les choix possibles et b) à présenter collectivement une image complète. Alors s'il vous plaît, arrêtez enfantinement de «jouer SO» et réfléchissez avant d'agir.
kostix
Réponse courte. Rédaction de code maintenable. Votre réponse ne peut pas être portée sur une plate-forme différente. Donc, si l'OP choisissait de déplacer son application vers une autre plate-forme, l'application serait interrompue. J'ai eu ma juste part de personnes qui ont écrit du code dépendant de la plate-forme où il est totalement inutile et cela crée plus de problèmes que ça en vaut la peine. Vous n'écrivez pas de code pour vous-même. Vous écrivez du code pour les personnes qui le maintiendront après votre départ. C'est pourquoi cette réponse n'est pas appropriée. Aucune raison de recourir aux ad hominems et de m'appeler enfantin.
revoir
1
@Byebye, j'ai réagi de manière excessive, alors veuillez m'excuser pour l'attaque. Je ne suis toujours pas convaincu de vos raisons, mais c'est censé être le cas «d'accord pour ne pas être d'accord».
kostix
1

Cette bibliothèque est notre standard pour la génération et l'analyse d'uuid:

https://github.com/pborman/uuid

James McGill
la source
Notez que la propre bibliothèque de Google ( github.com/google/uuid ) est partiellement basée sur github.com/pborman/uuid , qui, à son tour, a intégré certaines des modifications apportées par Google. Cependant, prétendument, si vous souhaitez contribuer à l' un de ces projets, vous devez signer (ou avoir signé) un accord de licence de contributeur (CLA). Ce n'était apparemment pas le cas en août 2015, lorsque votre réponse a été ajoutée; @pborman a ajouté cela uniquement le 16 février 2016 .
Gwyneth Llewelyn le