Go dili ile bir UUID oluşturmak için bir yöntem var mı


109

Şuna benzeyen kodum var:

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)

32 uzunluğunda bir dize döndürür, ancak bunun geçerli bir UUID olduğunu düşünmüyorum. Gerçek bir UUID ise, neden bir UUID ve kod olduğunu değiştirir değerinin amacı nedir u[8]ve u[6].

UUID oluşturmanın daha iyi bir yolu var mı?


1
Bu cevap şimdi daha uygun görünüyor.
ViKiG

Yanıtlar:


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

Bu çizgiler, bayt 6 ve 8'in değerlerini belirli bir aralığa sıkıştırır. rand.ReadAralıktaki rastgele baytları döndürür 0-255ve bunlar bir UUID için tüm geçerli değerler değildir. Anlayabildiğim kadarıyla bu, dilimdeki tüm değerler için yapılmalı.

Linux'taysanız, alternatif olarak arayabilirsiniz /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)
}

Hangi sonuç:

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

23
Özellikle, bu yaklaşım yavaştır; 2012 MacBook Air'de bu strateji yalnızca 170 uuids / saniye üretebilir.
Jay Taylor

12
Ve nu7hatch / gouuid kitaplığını kullanarak 172.488 uuids / saniye üretebildim.
Jay Taylor

2
u[6]Ve u[8]baytların iyi açıklaması .
chowey

3
Sistemimde (Ubuntu 15.10) ayrıca komut çıktısını dizeler aracılığıyla çalıştırmam gerekiyordu. Satırsonu karakterini kaldırmak için düzelt (string (out)), aksi takdirde sonda girildi mi? dosya sistemindeki karakter.
gregtczap

39
Var olan veya olmayan harici bir programı çağırmak, bu oldukça basit görevi yerine getirmenin korkunç bir yoludur.
Timmmm

96

Go-uuid kitaplığını kullanarak UUID'ler oluşturabilirsiniz . Bu, şunlarla kurulabilir:

go get github.com/nu7hatch/gouuid

Aşağıdakilerle rastgele (sürüm 4) UUID'ler oluşturabilirsiniz:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

Döndürülen UUIDtür 16 baytlık bir dizidir, bu nedenle ikili değeri kolayca alabilirsiniz. Ayrıca, String()yöntemi aracılığıyla standart onaltılık dizge gösterimi sağlar .

Sahip olduğunuz kod aynı zamanda geçerli bir sürüm 4 UUID oluşturacak gibi görünüyor: sonunda gerçekleştirdiğiniz bitsel işlem, UUID'nin sürümünü ve varyant alanlarını onu sürüm 4 olarak doğru şekilde tanımlamak için ayarlar . Bu, rastgele UUID'leri diğer algoritmalar (örneğin, MAC adresinize ve zamanınıza bağlı olarak sürüm 1 UUID'ler) aracılığıyla oluşturulanlardan ayırmak için yapılır.


2
@ Ne yaptıklarını bilmeyenler için bu büyük olasılıkla doğrudur. Gereksiz bağımlılıkları ortaya çıkarmak her zaman kötü bir şeydir.
Erik Aigner

31
@ErikAigner 50 satır olduğu sürece düşünmem, yazmam ve test etmem gerekmiyor, onları alacağım teşekkür ederim .. Yapacak başka işlerim var sonra tekerleği yeniden icat ediyorum.
RickyA

3
Bu kitaplık aslında RFC4122 uyumlu değil gibi görünüyor: github.com/nu7hatch/gouuid/issues/28 (şu anda 2/1/2016 itibarıyla açık)
Charles L.

1
@ErikAigner'ın tekerleği yeniden icat etmesi de biraz gereksiz. Bir kütüphane varsa ve bunu iyi yapıyorsa, nasıl yapılacağını öğrenmek için yapıyorsanız, neden kendi işinizi kendiniz yapın.
Efendim

4
@ErikAigner bunu saçma buluyorum. Daha iyisini yapamadığınız veya programınıza özel bir şeye ihtiyaç duymadığınız sürece kimse zaten yapılmış şeyleri yeniden icat etmiyor, kodu inceliyor ve iyi yaptığını görüyorsanız neden kendiniz yapın - yalnızca geliştirme süresini ve maliyetini boşa harcamakla kalmazsınız, aynı zamanda Ne yaptığınızı tam olarak bilmiyorsanız, potansiyel olarak hatalar veya basitçe yanlış uygulamalar getirebilir, bu kütüphaneler genellikle ne yaptıklarını bilen insanlar yapılır. Üçüncü şahıs kitaplıklarını kullanmak çaylak değildir, sadece onun çalıştığını varsayan ve önce kodu incelemeyen tek çaylaktır ..
Efendim

70

go-uuidKütüphane RFC4122 uyumlu DEĞİLDİR. Varyant bitleri doğru ayarlanmadı. Topluluk üyeleri tarafından bunu düzeltmek için birkaç girişimde bulunuldu, ancak düzeltme için çekme istekleri kabul edilmiyor.

Kitaplığa dayalı olarak yeniden yazdığım Go uuid kitaplığını kullanarak UUID'ler oluşturabilirsiniz go-uuid. Birkaç düzeltme ve iyileştirme var. Bu, şunlarla kurulabilir:

go get github.com/twinj/uuid

Aşağıdakilerle rastgele (sürüm 4) UUID'ler oluşturabilirsiniz:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Döndürülen UUID türü bir arabirimdir ve temel alınan tür bir dizidir.

Kitaplık ayrıca v1 UUID'leri oluşturur ve v3 ve 5 UUID'leri doğru şekilde oluşturur. Yazdırma ve biçimlendirmeye yardımcı olacak birkaç yeni yöntem ve ayrıca mevcut verilere dayalı olarak UUID'ler oluşturmak için yeni genel yöntemler vardır.


4
Bu paketi beğendim. Tüm uygulamalarım için resmi olarak kabul ettim. Nu7hatch paketinin RFC4122 uyumlu olmadığını buldum.
Richard Eng

+1 Kabul edildi, güncellemeler ve baskı / biçimlendirme uzantıları zaten dahil edildi.
eduncan911

4
Sorumluluk reddi beyanı eksik mi? : p
chakrit

3
"Aşağıdaki" kütüphane nedir? SO üzerinde yukarı ve aşağı kullanmaktan kaçınmalısınız, çünkü bu oldukça hızlı bir şekilde değişebilir.
Stephan Dollberg

Ayrıca başka bir eşdeğer, satori / go.uuid var . Henüz denemedim ama nu7hatch ölü projesinin yerine kullanacağım ...
shadyyx

52

"kripto / rand", rastgele bayt üretimi için çapraz platform paketidir

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_uuidMAC adresi ve RFC4122 gibi rastgele olmayan tanımlayıcılar eksik olduğu için? Yani aslında daha rastgele.
Xeoncross

2
iyi cevap; Bunu stackoverflow.com/a/48134820/1122270 adresinde genişlettim ve pek çok insanın aslında UUID'leri (ya da kendi rastgele kullanmam gerektiğini düşündüğüm sha1 / sha256'yı) kullanmasına gerek olmadığını düşünüyorum. kimlik problemi), ancak rastgele ve benzersiz bir şey istersiniz ve örneğiniz bir çözüm için iyi bir başlangıç ​​sağlar
cnst

Teşekkürler! Yeterince basit
Karl Pokus

1. Bu, herhangi bir standarda uymuyor 2. Sadece %x128'den küçük bayt değerleriyle ilgili sorunlar var, doldurma uygulamanız gerekiyor, yani %04xbir bayt çifti için
Ja͢ck

38

Google tarafından resmi bir uygulama var: https://github.com/google/uuid

Bir sürüm 4 UUID oluşturmak şu şekilde çalışır:

package main

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

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

Burada deneyin: https://play.golang.org/p/6YPi1djUMj9


1
Godoc kullanılmasını önerir New()ve eşdeğerdiruuid.Must(uuid.NewRandom())
Jim

@ Jim: haklısın! Cevabımı buna göre güncelledim.
shutefan

New () 'in "ölümcül" olabileceğini unutmayın (bazı durumlarda bu sorun olmaz). Programınızın ölümcül olmasını istemediğiniz durumlarda sadece uuid.NewRandom () kullanın - bu bir UUID ve bir hata döndürür.
Tomer

@Tomer: doğru! Bunun hangi koşullarda gerçekleşeceğini merak etsem de. Bu, kodun ilgili kısmıdır: github.com/google/uuid/blob/… Varsayılan olarak okuyucu bir rand.Reader. Bunun bir hata
döndürüp

1
Merhaba @shutefan - Nadir olabileceğini kabul ediyorum. rand.Reader Kernel işlevlerini çağırır ( golang.org/src/crypto/rand/rand.go ). Bunlar belirli senaryolarda başarısız olabilir.
Tomer


12

Russ Cox'un gönderisinden :

Resmi kütüphane yok. Hata kontrolünü göz ardı ederek, bu iyi çalışacak gibi görünüyor:

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

Not: Orijinal Go 1 öncesi sürümde ilk satır şuydu:

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

Burada derler ve çalıştırır, sadece /dev/urandomoyun alanındaki tüm sıfırları döndürür. Yerel olarak iyi çalışmalıdır.

Aynı iş parçacığında başka yöntemler / referanslar / paketler de bulunur.


12
Ancak bu geçerli bir UUID oluşturmaz: sürüm 4 UUID'leri (rastgele verilere dayalı tür), rastgele olmayan UUID biçimleriyle çakışmayı önlemek için birkaç bitin belirli bir şekilde ayarlanmasını gerektirir.
James Henstridge

4
import "crypto/rand"Bence kullanmak daha iyi , ancak için +1 uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). OP'nin koduyla birleştirildiğinde bu harika çalışıyor.
chowey

2
Crypto / rand paketini kullanarak: play.golang.org/p/7JJDx4GL77 . zzzz'in kodu, crypt / rand'ın yaptığını yapar, ancak / dev / urandom'u (Windows) desteklemeyen platformları da kapsar.
Drew

Bunun platforma özgü olduğu unutulmamalıdır
Dan Esparza

2
@Matt: Sorun şu ki, diğer UUID formatları başka bir yetkiliye delege ederek (örneğin, Ethernet MAC adresinizin benzersiz olması) ve sonra bunu başka bir şeyle birleştirerek (örneğin zaman artı bir sayaç) benzersizliklerini elde ediyorlar. V4 olarak doğru şekilde biçimlendirilmemiş rastgele bir UUID üretirseniz, sistemi zayıflatırsınız.
James Henstridge

8

Uuid spesifikasyonunun bir parçası olarak, rastgele bir uuid üretirseniz, 13. karakter olarak bir "4" ve 17. karakterde ( kaynak ) "8", "9", "a" veya "b" içermelidir .

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

gorand paketi UUID'si yöntem bu duruma göre bir (rastgele oluşturulmuş) Sürüm 4 UUID'sine kanonik dize gösteriminde ( "xxxxxxxxxxxx xxxxxxxx-xxxxxxxxxxxx") ve 's RFC 4122 uyumlu bulunmaktadır.

Ayrıca, Go tarafından desteklenen tüm platformlarda kriptografik olarak en güvenli UUID üretimini sağlamak için kripto / rand paketini kullanır.

import "github.com/leonelquinteros/gorand"

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

    println(uuid)
} 

4

Linux'ta aşağıdakilerden okuyabilirsiniz /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))
}

Dış bağımlılık yok!

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

4
Olumsuz oy verildi çünkü çoklu platform uygulamaları için kullanılan bir programlama dilindeki bir ana bilgisayar platformuna doğrudan bağlı olmak, harici bir bağımlılıktan daha kötüdür.
Byebye

1
Programlama dili çoklu platform olabilir, ancak Linux'a özgü çok yaygın çözümlerdir ve hiçbir zaman başka platformlarda olmayacaklardır, bu nedenle geçerli bir yanıt IMO'dur.
ton

1

Windows için yakın zamanda şunu yaptım:

// +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
Olumsuz oy verildi çünkü çoklu platform uygulamaları için kullanılan bir programlama dilindeki bir ana bilgisayar platformuna doğrudan bağlı olmak, harici bir bağımlılıktan daha kötüdür.
Byebye

1
@Byebye, bu soruya verilen tüm yanıtları gözden geçirmek ve "sisteme bağlı" olanların tümünü olumsuz oylamak için neyin "daha kötü" olduğuna (ve neyin olmadığına) karar vermek için neden kendinizi bir otorite olarak gördüğünüzü merak ediyorum. Bu cevaplar a) tüm olası seçeneklerin ufkunu genişletmek ve b) toplu olarak tam bir resim sunmak için verildi . Bu yüzden lütfen çocukça "SO oynamayı" bırakın ve harekete geçmeden önce biraz düşünün.
kostix

Kısa cevap. Bakım yapılabilir kod yazma. Cevabınız farklı bir platforma taşınamaz. Dolayısıyla, OP, uygulamasını başka bir platforma taşımayı seçerse, uygulama bozulur. Tamamen gereksiz olduğu platforma bağlı kod yazan kişilerden adil bir pay aldım ve bu, değerinden daha fazla sorun yaratır. Sadece kendiniz için kod yazmazsınız. Siz gittikten sonra onu koruyacak insanlar için kod yazarsınız. Bu yüzden bu cevap uygun değil. Hominemlere başvurup bana çocukça demeye gerek yok.
Byebye

1
@Byebye, aşırı tepki verdim, bu yüzden lütfen saldırı için beni affedin. Yine de nedenleriniz konusunda ikna olmadım, ama sözde bu "anlaşamama konusunda anlaşalım" davası.
kostix

1

Bu kitaplık, uuid oluşturma ve ayrıştırma için standartımızdır:

https://github.com/pborman/uuid


Google'ın kendi kitaplığının ( github.com/google/uuid ) kısmen github.com/pborman/uuid'e dayandığını ve bunun da Google'ın yaptığı bazı değişiklikleri geri aldığını unutmayın. Ancak, iddiaya göre, bu projelerden herhangi birine katkıda bulunmak istiyorsanız, bir Katılımcı Lisans Anlaşması (CLA) imzalamanız (veya imzalamanız) gerekir . Görünüşe göre, cevabınız eklendiğinde, Ağustos 2015'te durum böyle değildi; @pborman bunu yalnızca 16 Feb 2016'da ekledi .
Gwyneth Llewelyn
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.