Czy istnieje metoda generowania UUID za pomocą języka go

109

Mam kod, który wygląda następująco:

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)

Zwraca ciąg o długości 32, ale nie sądzę, że jest to poprawny identyfikator UUID. Jeśli jest to prawdziwy identyfikator UUID, dlaczego jest to identyfikator UUID i jaki jest cel kodu modyfikującego wartość u[8]i u[6].

Czy istnieje lepszy sposób generowania UUID?

hardPass
źródło
1
Ta odpowiedź wydaje się teraz bardziej trafna.
ViKiG

Odpowiedzi:

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

Te linie ograniczają wartości bajtów 6 i 8 do określonego zakresu. rand.Readzwraca losowe bajty z zakresu 0-255, które nie są poprawnymi wartościami identyfikatora UUID. O ile wiem, należy to zrobić dla wszystkich wartości w wycinku.

Jeśli korzystasz z Linuksa, możesz alternatywnie zadzwonić /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)
}

Co daje:

$ go run uuid.go 
dc9076e9-2fda-4019-bd2c-900a8284b9c4
jimt
źródło
23
Warto zauważyć, że to podejście jest powolne; na MacBooku Air z 2012 roku ta strategia może wyprodukować tylko 170 uuidów na sekundę.
Jay Taylor
12
Korzystając z biblioteki nu7hatch / gouuid, udało mi się wygenerować 172 488 uuidów na sekundę.
Jay Taylor
2
Dobre wyjaśnienie bajtów u[6]i u[8].
chowey
3
W moim systemie (Ubuntu 15.10) musiałem również uruchomić wyjście polecenia za pomocą ciągów. Trim (string (out)), aby usunąć znak nowego wiersza, w przeciwnym razie został on wprowadzony jako końcowy? znak w systemie plików.
gregtczap
39
Wywołanie zewnętrznego programu, który może istnieć lub nie, jest okropnym sposobem wykonania tego dość prostego zadania.
Timmmm
96

Możesz generować UUID za pomocą biblioteki go-uuid . Można to zainstalować za pomocą:

go get github.com/nu7hatch/gouuid

Możesz generować losowe (wersja 4) UUID za pomocą:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

Zwracany UUIDtyp to 16-bajtowa tablica, dzięki czemu można łatwo pobrać wartość binarną. Zapewnia również standardową reprezentację ciągu szesnastkowego poprzez swoją String()metodę.

Wygląda również na to, że kod, który posiadasz, wygeneruje również prawidłowy identyfikator UUID wersji 4: manipulacja bitowa, którą wykonujesz na końcu, ustawia pola wersji i wariantów UUID, aby poprawnie zidentyfikować go jako wersję 4 . Ma to na celu odróżnienie losowych identyfikatorów UUID od tych generowanych przez inne algorytmy (np. UUID wersji 1 na podstawie adresu MAC i czasu).

James Henstridge
źródło
2
@Flimzy dla osób, które nie wiedzą, co robią, najprawdopodobniej jest prawdą. Wprowadzanie niepotrzebnych zależności jest zawsze czymś złym.
Erik Aigner
31
@ErikAigner Tak długo, jak jest to 50 linii, o których nie muszę myśleć, pisać i testować, wezmę je, dziękuję .. Mam inne rzeczy do zrobienia, a potem wymyślenie koła na nowo.
RickyA
3
Wygląda na to, że ta biblioteka nie jest w rzeczywistości zgodna z RFC4122: github.com/nu7hatch/gouuid/issues/28 (aktualnie otwarte wydanie na dzień 2/1/2016)
Charles L.
1
@ErikAigner odkrywanie na nowo koła jest również trochę niepotrzebne. Jeśli biblioteka istnieje i robi to dobrze, po co zawracać sobie głowę czymś innym, niż jeśli robisz to, aby nauczyć się, jak to zrobić.
Sir
4
@ErikAigner po prostu uważam to za śmieszne. Nikt nie wymyśla na nowo rzeczy, które już zostały zrobione, chyba że możesz zrobić lepiej lub potrzebujesz czegoś konkretnego dla swojego programu, jeśli sprawdzisz kod i zobaczysz, że robi to dobrze, po co robić to samodzielnie - nie tylko marnujesz czas i koszty na rozwój, ale także potencjalnie wprowadzając błędy lub po prostu złe implementacje, jeśli nie wiesz dokładnie, co robisz, te biblioteki są zwykle tworzone przez ludzi, którzy wiedzą, co robią. Nie jest debiutantem do korzystania z bibliotek stron trzecich, jego jedynym debiutantem jest po prostu założenie, że działa i nie sprawdzanie najpierw kodu ..
Sir
70

Plik go-uuidBiblioteka nie jest zgodny z RFC4122. Bity wariantu nie są ustawione poprawnie. Członkowie społeczności podjęli kilka prób, aby to naprawić, ale żądania ściągnięcia poprawki nie są akceptowane.

Możesz generować UUID za pomocą biblioteki Go uuid, którą przepisałem na podstawie tej go-uuidbiblioteki. Istnieje kilka poprawek i ulepszeń. Można to zainstalować za pomocą:

go get github.com/twinj/uuid

Możesz generować losowe (wersja 4) UUID za pomocą:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Zwrócony typ UUID jest interfejsem, a typem bazowym jest tablica.

Biblioteka generuje również identyfikatory UUID v1 i poprawnie generuje identyfikatory UUID v3 i 5. Istnieje kilka nowych metod ułatwiających drukowanie i formatowanie, a także nowe ogólne metody tworzenia identyfikatorów UUID na podstawie istniejących danych.

twinj
źródło
4
Podoba mi się ten pakiet. Oficjalnie zaadoptowałem go do wszystkich moich aplikacji. Okazało się, że pakiet nu7hatch nie jest zgodny z RFC4122.
Richard Eng
+1 Zgoda, aktualizacje i rozszerzenia drukowania / formatowania są już uwzględnione.
eduncan911
4
Brak wyłączenia odpowiedzialności? : p
chakrit
3
Co to jest biblioteka „pod”? Powinieneś unikać używania powyżej i poniżej na SO, ponieważ może to zmienić się dość szybko.
Stephan Dollberg
Jest też inny odpowiednik, satori / go.uuid . Nie próbowałem go jeszcze, ale mam zamiar używać go jako zamiennik nu7hatch martwym projektem ...
shadyyx
52

„crypto / rand” to wieloplatformowy pakiet do generowania losowych bajtów

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
źródło
3
pseudo_uuidponieważ brakuje nielosowych identyfikatorów, takich jak adres MAC i cokolwiek innego, co określono w RFC4122? Więc jest to bardziej przypadkowe.
Xeoncross
2
dobra odpowiedź; Rozszerzyłem go na stackoverflow.com/a/48134820/1122270 i myślę, że wielu ludzi w rzeczywistości nie musi specjalnie używać identyfikatorów UUID (ani sha1 / sha256, które, jak sądziłem, muszę użyć do własnego losowego- id problem), ale po prostu chcesz czegoś losowego i unikalnego, a twoja próbka zapewnia dobry początek rozwiązania
cnst
Dzięki! Dość proste
Karl Pokus
1. To nie jest zgodne z żadnym standardem 2. Używanie %xma tylko problemy z wartościami bajtów mniejszymi niż 128, musisz zastosować dopełnienie, np. %04xDla pary bajtów
Ja͢ck
38

Istnieje oficjalna implementacja Google: https://github.com/google/uuid

Generowanie UUID wersji 4 działa w następujący sposób:

package main

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

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

Wypróbuj tutaj: https://play.golang.org/p/6YPi1djUMj9

shutefan
źródło
1
Godoc zaleca stosowanie New()i jest to równoważneuuid.Must(uuid.NewRandom())
Jima
@Jim: masz rację! Odpowiednio zaktualizowałem swoją odpowiedź.
shutefan
Zauważ, że New () może być „fatalny” (co w niektórych przypadkach jest w porządku). W przypadkach, gdy nie chcesz, aby Twój program był fatalny, po prostu użyj uuid.NewRandom () - która zwraca UUID i błąd.
Tomer
@Tomer: prawda! Chociaż zastanawiam się, w jakich okolicznościach to się faktycznie wydarzy. To jest odpowiednia część kodu: github.com/google/uuid/blob/… Domyślnie czytnikiem jest plik rand.Reader. Nie jestem pewien, czy kiedykolwiek zwróci błąd, czy może się to zdarzyć tylko z niestandardowym czytnikiem ...
shutefan
1
Cześć @shutefan - zgadzam się, że może to być rzadkie. rand.Reader wywołuje funkcje jądra ( golang.org/src/crypto/rand/rand.go ). W niektórych sytuacjach mogą się one nie powieść.
Tomer
12

Z posta Russa Coxa :

Nie ma oficjalnej biblioteki. Ignorując sprawdzanie błędów, wygląda na to, że działałoby dobrze:

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

Uwaga: w oryginalnej wersji Go 1 pierwsza linia była:

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

Tutaj tylko kompiluje i wykonuje/dev/urandom zwraca wszystkie zera na placu zabaw. Lokalnie powinno działać dobrze.

W tym samym wątku znajduje się kilka innych metod / odwołań / pakietów.

zzzz
źródło
12
Nie spowoduje to jednak wygenerowania prawidłowego UUID: UUID wersji 4 (typ oparty na danych losowych) wymaga ustawienia kilku bitów w określony sposób, aby uniknąć konfliktu z nielosowymi formatami UUID.
James Henstridge
4
import "crypto/rand"Moim zdaniem lepiej używać , ale +1 za uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). W połączeniu z kodem OP, a to działa świetnie.
chowey
2
Korzystanie z pakietu crypto / rand: play.golang.org/p/7JJDx4GL77 . Kod zzzz robi to, co robi crypt / rand, z wyjątkiem tego, że obejmuje również platformy, które nie obsługują / dev / urandom (Windows).
Drew
Należy zauważyć, że jest to specyficzne dla platformy
Dan Esparza
2
@Matt: problem polega na tym, że inne formaty UUID uzyskują swoją niepowtarzalność poprzez delegowanie do jakiejś innej instytucji (np. Że twój adres MAC w sieci Ethernet jest unikalny), a następnie łączenie tego z czymś innym (np. Czas plus licznik). Jeśli utworzysz losowy identyfikator UUID, który nie jest poprawnie sformatowany jako V4, osłabiasz system.
James Henstridge
8

W ramach specyfikacji uuid, jeśli generujesz uuid z random, musi on zawierać „4” jako 13. znak i „8”, „9”, „a” lub „b” na 17. miejscu ( źródło ).

// 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
źródło
4

gorand opakowanie ma UUID metody zwracającej wersja 4 (losowo wygenerowanym) uuid IN kanonicznej ciąg znaków ( „XXXXXXXXXXXX-xxxxxxxx-XXXXXXXXXXXX”) i jest zgodny z RFC 4122.

Używa również pakietu crypto / rand, aby zapewnić najbardziej kryptograficznie bezpieczne generowanie identyfikatorów UUID na wszystkich platformach obsługiwanych przez Go.

import "github.com/leonelquinteros/gorand"

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

    println(uuid)
} 
peiiion
źródło
4

W systemie Linux można odczytać z /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))
}

Żadnych zewnętrznych zależności!

$ go run uuid.go 
3ee995e3-0c96-4e30-ac1e-f7f04fd03e44
soulshake
źródło
4
Obniżony, ponieważ bezpośrednie uzależnienie od platformy hosta w języku programowania używanym w aplikacjach wieloplatformowych jest gorsze niż zależność zewnętrzna.
widzenia,
1
Język programowania może być wieloplatformowy, ale jest to bardzo powszechne rozwiązania specyficzne dla Linuksa, które nigdy nie będą dostępne na innej platformie, więc jest to poprawna odpowiedź IMO.
ton
1

W przypadku systemu Windows ostatnio zrobiłem to:

// +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
źródło
2
Obniżony, ponieważ bezpośrednie uzależnienie od platformy hosta w języku programowania używanym w aplikacjach wieloplatformowych jest gorsze niż zależność zewnętrzna.
widzenia,
1
@ Do widzenia, zastanawiam się, dlaczego uważasz się za autorytet, który decyduje, co „jest gorsze” (a co nie), aby przejrzeć wszystkie odpowiedzi udzielone na to pytanie i przegłosować wszystkie, które są „zależne od systemu”? Odpowiedzi te miały na celu a) poszerzenie horyzontu wszystkich możliwych wyborów oraz b) wspólne przedstawienie pełnego obrazu. Więc proszę, przestańcie dziecinnie „bawić się w SO” i pomyślcie, zanim zaczniecie działać.
kostix
Krótka odpowiedź. Pisanie utrzymywalnego kodu. Twoja odpowiedź nie może zostać przeniesiona na inną platformę. Więc jeśli OP zdecyduje się przenieść swoją aplikację na inną platformę, aplikacja ulegnie awarii. Miałem sporo ludzi, którzy napisali kod zależny od platformy, gdzie jest to całkowicie niepotrzebne i stwarza więcej kłopotów, niż jest to warte. Nie piszesz kodu tylko dla siebie. Piszesz kod dla ludzi, którzy będą go obsługiwać, gdy odejdziesz. Dlatego ta odpowiedź nie jest właściwa. Nie ma powodu, by uciekać się do ad hominemów i nazywać mnie dziecinną.
widzenia,
1
@Byebye, przesadziłem, więc przepraszam za atak. Nie jestem przekonany co do twoich powodów, ale podobno jest to sprawa „zgódźmy się nie zgodzić”.
kostix
1

Ta biblioteka jest naszym standardem do generowania i analizowania uuidów:

https://github.com/pborman/uuid

James McGill
źródło
Zauważ, że własna biblioteka Google ( github.com/google/uuid ) jest częściowo oparta na github.com/pborman/uuid , który z kolei włączył z powrotem niektóre zmiany wprowadzone przez Google. Jednak rzekomo, jeśli chcesz wnieść swój wkład w którykolwiek z tych projektów, musisz podpisać (lub podpisać) umowę licencyjną współtwórcy (CLA). Najwyraźniej tak nie było w sierpniu 2015 r., Kiedy dodano Twoją odpowiedź; @pborman dodał, że dopiero 16 lutego 2016 r .
Gwyneth Llewelyn