Esiste un metodo per generare un UUID con go language


109

Ho un codice simile a questo:

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)

Restituisce una stringa con una lunghezza di 32, ma non credo che sia un UUID valido. Se è un UUID reale, perché è un UUID e qual è lo scopo del codice che modifica il valore di u[8]e u[6].

C'è un modo migliore per generare UUID?


1
Questa risposta sembra più appropriata ora.
ViKiG

Risposte:


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

Queste linee limitano i valori dei byte 6 e 8 a un intervallo specifico. rand.Readrestituisce byte casuali nell'intervallo 0-255, che non sono tutti valori validi per un UUID. Per quanto ne so, questo dovrebbe essere fatto per tutti i valori nella sezione.

Se sei su Linux, in alternativa puoi chiamare /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)
}

Che produce:

$ go run uuid.go 
dc9076e9-2fda-4019-bd2c-900a8284b9c4

23
In particolare, questo approccio è lento; su un MacBook Air del 2012 questa strategia può produrre solo 170 uuid / secondo.
Jay Taylor

12
E usando la libreria nu7hatch / gouuid, sono stato in grado di generare 172.488 uuid / secondo.
Jay Taylor

2
Buona spiegazione dei byte u[6]e u[8].
chowey

3
Sul mio sistema (Ubuntu 15.10) avevo anche bisogno di eseguire l'output del comando tramite stringhe.Trim (string (out)) per rimuovere il carattere di nuova riga, altrimenti è stato inserito come finale? carattere nel filesystem.
gregtczap

39
Chiamare un programma esterno che può o non può esistere è un modo orribile per svolgere questo compito abbastanza semplice.
Timmmm

96

Puoi generare UUID utilizzando la libreria go-uuid . Questo può essere installato con:

go get github.com/nu7hatch/gouuid

Puoi generare UUID casuali (versione 4) con:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

Il UUIDtipo restituito è un array di 16 byte, quindi puoi recuperare facilmente il valore binario. Fornisce inoltre la rappresentazione della stringa esadecimale standard tramite il suo String()metodo.

Il codice che hai sembra anche generare un UUID di versione 4 valido: la manipolazione bit per bit che esegui alla fine imposta i campi della versione e della variante dell'UUID per identificarlo correttamente come versione 4 . Questo viene fatto per distinguere gli UUID casuali da quelli generati tramite altri algoritmi (ad esempio gli UUID della versione 1 in base all'indirizzo MAC e all'ora).


2
@Flimzy per le persone che non sanno cosa stanno facendo, molto probabilmente è vero. L'introduzione di dipendenze non necessarie è sempre una cosa negativa.
Erik Aigner

31
@ErikAigner Finché sono 50 righe a cui non devo pensare, scrivere e testare, le prendo grazie .. Ho altre cose da fare quindi reinventare la ruota.
RickyA

3
Questa libreria sembra non essere effettivamente conforme a RFC4122: github.com/nu7hatch/gouuid/issues/28 (numero attualmente aperto il 2/1/2016)
Charles L.

1
@ErikAigner reinventare la ruota è anche un po 'inutile. Se una libreria esiste e funziona bene, perché preoccuparsi di farne una tua diversa se non lo fai per imparare a farlo.
Sir

4
@ ErikAigner trovo solo che riducibile. Nessuno reinventa le cose che sono già state fatte a meno che tu non possa fare di meglio o hai bisogno di qualcosa di specifico per il tuo programma, se controlli il codice e vedi che lo fa bene perché preoccuparti di farlo da solo - non solo sprechi tempo e costi di sviluppo, lo sei anche potenzialmente porteranno bug o implementazioni semplicemente sbagliate se non sai completamente cosa stai facendo, queste librerie sono solitamente create da persone che sanno cosa stanno facendo. Non è un principiante usare librerie di terze parti, è l'unico principiante a presumere che funzioni e non ispezionare prima il codice ..
Sir

70

Il go-uuid libreria NON è conforme a RFC4122. I bit delle varianti non sono impostati correttamente. Ci sono stati diversi tentativi da parte dei membri della comunità di risolvere questo problema, ma le richieste pull per la correzione non sono state accettate.

Puoi generare UUID utilizzando la libreria Go uuid che ho riscritto in base alla go-uuidlibreria. Sono disponibili diverse correzioni e miglioramenti. Questo può essere installato con:

go get github.com/twinj/uuid

Puoi generare UUID casuali (versione 4) con:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Il tipo di UUID restituito è un'interfaccia e il tipo sottostante è un array.

La libreria genera anche UUID v1 e genera correttamente UUID v3 e 5. Esistono diversi nuovi metodi per aiutare con la stampa e la formattazione e anche nuovi metodi generali per creare UUID basati sui dati esistenti.


4
Mi piace questo pacchetto. L'ho adottato ufficialmente per tutte le mie applicazioni. Ho scoperto che il pacchetto nu7hatch non era conforme a RFC4122.
Richard Eng

+1 D'accordo, gli aggiornamenti e le estensioni di stampa / formattazione già inclusi.
eduncan911

4
Disclaimer mancante? : p
chakrit

3
Cos'è la libreria "sotto"? Dovresti evitare di usare sopra e sotto su SO poiché ciò può cambiare abbastanza rapidamente.
Stephan Dollberg

C'è anche un altro equivalente, satori / go.uuid . Non l'ho ancora provato ma lo userò come sostituto del progetto morto nu7hatch ...
shadyyx

52

"crypto / rand" è un pacchetto multipiattaforma per la generazione casuale di byte

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
}

3
pseudo_uuidperché mancano gli identificatori non casuali come l'indirizzo MAC e qualsiasi altra cosa specificata da RFC4122? Quindi in realtà è più casuale.
Xeoncross

2
buona risposta; L'ho espanso su stackoverflow.com/a/48134820/1122270 e penso che molte persone in realtà non abbiano bisogno di utilizzare gli UUID in modo specifico (né lo sha1 / sha256 che pensavo di dover usare per il mio caso- ID problema), ma vuoi semplicemente qualcosa di casuale e unico, e il tuo campione fornisce un buon inizio per una soluzione
cnst

Grazie! Abbastanza semplice
Karl Pokus

1. Questo non è conforme a nessuno standard 2. L'utilizzo %xha solo problemi con valori di byte inferiori a 128, è necessario applicare il riempimento, ad esempio %04xper una coppia di byte
Ja͢ck

38

C'è un'implementazione ufficiale di Google: https://github.com/google/uuid

La generazione di un UUID versione 4 funziona in questo modo:

package main

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

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

Provalo qui: https://play.golang.org/p/6YPi1djUMj9


1
Il godoc consiglia di utilizzare New()ed è equivalente auuid.Must(uuid.NewRandom())
Jim

@ Jim: hai ragione! Ho aggiornato di conseguenza la mia risposta.
shutefan

Notare che New () può "fatale" (il che in alcuni casi va bene). Nei casi in cui non vuoi che il tuo programma diventi fatale usa semplicemente uuid.NewRandom () - che restituisce un UUID e un errore.
Tomer

@ Tom: vero! Anche se mi chiedo in quali circostanze ciò sarebbe realmente accaduto. Questa è la parte rilevante del codice: github.com/google/uuid/blob/… Per impostazione predefinita il lettore è un rand.Reader. Non sono sicuro se quello restituirà mai un errore o se questo può accadere solo con un Reader personalizzato ...
shutefan

1
Ciao @shutefan, sono d'accordo sul fatto che potrebbe essere raro. rand.Reader chiama le funzioni del kernel ( golang.org/src/crypto/rand/rand.go ). Questi potrebbero non riuscire in determinati scenari.
Tomer


12

Dal post di Russ Cox :

Non esiste una biblioteca ufficiale. Ignorando il controllo degli errori, sembra che funzionerebbe bene:

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

Nota: nella versione originale, pre Go 1, la prima riga era:

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

Qui compila ed esegue, solo/dev/urandom restituisce tutti zeri nel playground. Dovrebbe funzionare bene a livello locale.

Nello stesso thread sono stati trovati altri metodi / riferimenti / pacchetti.


12
Questo però non genererà un UUID valido: gli UUID della versione 4 (il tipo basato su dati casuali) richiedono che alcuni bit siano impostati in un certo modo per evitare conflitti con i formati UUID non casuali.
James Henstridge,

4
Meglio usare import "crypto/rand"secondo me, ma +1 per uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). Combinato con il codice dell'OP, e funziona alla grande.
chowey

2
Utilizzando il pacchetto crypto / rand: play.golang.org/p/7JJDx4GL77 . Il codice di zzzz fa quello che fa crypt / rand, tranne per il fatto che copre anche piattaforme che non supportano / dev / urandom (Windows).
Drew

Va notato che questo è specifico della piattaforma
Dan Esparza

2
@ Matt: il problema è che gli altri formati UUID ottengono la loro unicità delegando a qualche altra autorità (es. Che il tuo indirizzo MAC Ethernet è univoco), e poi combinandolo con qualcos'altro (es. L'ora più un contatore). Se produci un UUID casuale che non è formattato correttamente come V4, stai indebolendo il sistema.
James Henstridge

8

Come parte delle specifiche uuid, se si genera un uuid da casuale, deve contenere un "4" come 13 ° carattere e un "8", "9", "a" o "b" nel 17 ° ( fonte ).

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

4

Il pacchetto gorand ha un metodo UUID che restituisce un UUID versione 4 (generato casualmente) nella sua rappresentazione di stringa canonica ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") ed è conforme a RFC 4122.

Utilizza anche il pacchetto crypto / rand per garantire la generazione più sicura crittograficamente di UUID su tutte le piattaforme supportate da Go.

import "github.com/leonelquinteros/gorand"

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

    println(uuid)
} 

4

Su Linux puoi leggere da /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))
}

Nessuna dipendenza esterna!

$ go run uuid.go 
3ee995e3-0c96-4e30-ac1e-f7f04fd03e44

4
Downvoted perché dipendere direttamente da una piattaforma host in un linguaggio di programmazione utilizzato per applicazioni multipiattaforma è peggiore di una dipendenza esterna.
Byebye

1
Il linguaggio di programmazione può essere multipiattaforma, ma sono soluzioni molto comuni specifiche per Linux che non saranno mai su altre piattaforme, quindi è una risposta valida IMO.
ton

1

Per Windows, ho fatto di recente questo:

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

2
Downvoted perché dipendere direttamente da una piattaforma host in un linguaggio di programmazione utilizzato per applicazioni multipiattaforma è peggiore di una dipendenza esterna.
Byebye

1
@ Byebye, mi chiedo perché ti consideri un'autorità per decidere cosa "è peggio" (e cosa non lo è) per scorrere tutte le risposte date a questa domanda e sottovalutare tutte quelle che sono "dipendenti dal sistema"? Queste risposte sono state date per a) ampliare l'orizzonte di tutte le scelte possibili eb) presentare collettivamente un quadro completo. Quindi per favore smettila infantilmente di "giocare a SO" e metti un po 'di pensiero prima di agire.
kostix

Risposta breve. Scrittura di codice gestibile. La tua risposta non può essere trasferita su una piattaforma diversa. Quindi, se l'OP scegliesse di spostare la propria applicazione su un'altra piattaforma, l'applicazione si interromperà. Ho avuto la mia giusta quota di persone che hanno scritto codice dipendente dalla piattaforma in cui è totalmente inutile e crea più problemi di quanto ne valga la pena. Non scrivi codice solo per te stesso. Scrivi codice per le persone che lo manterranno dopo che te ne sarai andato. Ecco perché questa risposta non è appropriata. Nessun motivo per ricorrere ad hominems e chiamarmi infantile.
Byebye

1
@ Byebye, ho reagito in modo eccessivo, quindi per favore scusami per l'attacco. Non sono ancora convinto delle tue ragioni, ma presumibilmente è il caso "acconsentiamo a non essere d'accordo".
kostix

1

Questa libreria è il nostro standard per la generazione e l'analisi di uuid:

https://github.com/pborman/uuid


Nota che la libreria di Google ( github.com/google/uuid ) è parzialmente basata su github.com/pborman/uuid , che, a sua volta, ha incorporato alcune delle modifiche apportate da Google. Tuttavia, presumibilmente, se desideri contribuire a uno di questi progetti, devi firmare (o aver firmato) un contratto di licenza per collaboratore (CLA). Apparentemente non era così nell'agosto 2015, quando è stata aggiunta la tua risposta; @pborman lo ha aggiunto solo il 16 febbraio 2016 .
Gwyneth Llewelyn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.