Apakah ada metode untuk menghasilkan UUID dengan bahasa go

109

Saya memiliki kode yang terlihat seperti ini:

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)

Ia mengembalikan string dengan panjang 32, tapi saya rasa itu bukan UUID yang valid. Jika itu adalah UUID yang sebenarnya, mengapa itu UUID, dan apa tujuan kode yang mengubah nilai u[8]dan u[6].

Apakah ada cara yang lebih baik untuk menghasilkan UUID?

hardPass
sumber
1
Jawaban ini tampaknya lebih tepat sekarang.
ViKiG

Jawaban:

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

Garis-garis ini menjepit nilai byte 6 dan 8 ke kisaran tertentu. rand.Readmengembalikan byte acak dalam rentang tersebut 0-255, yang tidak semuanya merupakan nilai yang valid untuk UUID. Sejauh yang saya tahu, ini harus dilakukan untuk semua nilai dalam irisan.

Jika Anda menggunakan linux, Anda dapat menelepon /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)
}

Hasil yang mana:

$ go run uuid.go 
dc9076e9-2fda-4019-bd2c-900a8284b9c4
jimt
sumber
23
Khususnya, pendekatan ini lambat; pada MacBook Air 2012, strategi ini hanya dapat menghasilkan 170 cairan / detik.
Jay Taylor
12
Dan menggunakan pustaka nu7hatch / gouuid, saya dapat menghasilkan 172.488 uuids / detik.
Jay Taylor
2
Penjelasan yang bagus tentang u[6]dan u[8]byte.
chowey
3
Di sistem saya (Ubuntu 15.10), saya juga perlu menjalankan output perintah melalui string. Trim (string (keluar)) untuk menghapus karakter baris baru, jika tidak maka akan dimasukkan sebagai trailing? karakter dalam sistem file.
gregtczap
39
Memanggil program eksternal yang mungkin ada atau tidak ada adalah cara yang buruk untuk melakukan tugas yang cukup sederhana ini.
Timmmm
96

Anda dapat membuat UUID menggunakan pustaka go-uuid . Ini dapat diinstal dengan:

go get github.com/nu7hatch/gouuid

Anda dapat membuat UUID (versi 4) acak dengan:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

UUIDJenis yang dikembalikan adalah array 16 byte, sehingga Anda dapat mengambil nilai biner dengan mudah. Ini juga menyediakan representasi string hex standar melalui String()metodenya.

Kode yang Anda miliki juga terlihat seperti itu juga akan menghasilkan UUID versi 4 yang valid: manipulasi bitwise yang Anda lakukan di bagian akhir mengatur bidang versi dan varian dari UUID untuk mengidentifikasinya dengan benar sebagai versi 4 . Ini dilakukan untuk membedakan UUID acak dari yang dihasilkan melalui algoritme lain (mis. UUID versi 1 berdasarkan alamat MAC dan waktu Anda).

James Henstridge
sumber
2
@Flimzy untuk orang yang tidak tahu apa yang mereka lakukan, itu kemungkinan besar benar. Memperkenalkan dependensi yang tidak perlu selalu merupakan hal yang buruk.
Erik Aigner
31
@ErikAigner Selama 50 baris saya tidak perlu memikirkan, menulis dan menguji, saya akan menerimanya terima kasih .. Saya memiliki hal-hal lain yang harus dilakukan kemudian menemukan kembali roda.
RickyA
3
Library ini sepertinya tidak benar-benar mematuhi RFC4122: github.com/nu7hatch/gouuid/issues/28 ( terbitan saat ini terbuka pada 2/1/2016)
Charles L.
1
@ErikAigner menciptakan kembali roda juga agak tidak perlu. Jika perpustakaan ada dan melakukannya dengan baik mengapa repot-repot melakukannya sendiri selain jika Anda melakukannya untuk mempelajari cara melakukannya.
Tuan
4
@ErikAigner saya hanya menemukan itu konyol. Tidak ada yang menemukan kembali hal-hal yang sudah dilakukan kecuali Anda dapat melakukan lebih baik atau membutuhkan sesuatu yang spesifik untuk program Anda, jika Anda memeriksa kode dan melihatnya melakukannya dengan baik mengapa repot-repot melakukannya sendiri - Anda tidak hanya membuang waktu dan biaya pengembangan, Anda juga berpotensi membawa bug atau implementasi yang salah jika Anda tidak benar-benar tahu apa yang Anda lakukan, perpustakaan ini biasanya dibuat orang-orang yang tahu apa yang mereka lakukan. Bukan pemula untuk menggunakan perpustakaan pihak ketiga, hanya pemula yang menganggapnya berfungsi dan tidak memeriksa kodenya terlebih dahulu ..
Sir
70

The go-uuidperpustakaan TIDAK RFC4122 compliant. Bit varian tidak disetel dengan benar. Ada beberapa upaya oleh anggota komunitas untuk memperbaikinya tetapi permintaan penarikan untuk perbaikan tidak diterima.

Anda dapat membuat UUID menggunakan pustaka Go uuid yang saya tulis ulang berdasarkan go-uuidpustaka. Ada beberapa perbaikan dan peningkatan. Ini dapat diinstal dengan:

go get github.com/twinj/uuid

Anda dapat membuat UUID (versi 4) acak dengan:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Jenis UUID yang dikembalikan adalah antarmuka dan jenis yang mendasarinya adalah array.

Pustaka juga menghasilkan UUID v1 dan dengan benar menghasilkan v3 dan 5 UUID. Ada beberapa metode baru untuk membantu pencetakan dan pemformatan dan juga metode umum baru untuk membuat UUID berdasarkan data yang ada.

twinj
sumber
4
Saya suka paket ini. Saya telah secara resmi mengadopsinya untuk semua aplikasi saya. Saya menemukan bahwa paket nu7hatch tidak sesuai dengan RFC4122.
Richard Eng
+1 Setuju, pembaruan dan ekstensi pencetakan / format sudah disertakan.
eduncan911
4
Penafian hilang? : p
chakrit
3
Apa perpustakaan "di bawah"? Anda harus menghindari penggunaan di atas dan di bawah SO karena itu dapat berubah cukup cepat.
Stephan Dollberg
Ada juga equivallent lainnya, satori / go.uuid . Belum mencobanya tetapi saya akan menggunakannya sebagai pengganti proyek mati nu7hatch ...
shadyyx
52

"crypto / rand" adalah pkg lintas platform untuk pembuatan byte acak

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
sumber
3
pseudo_uuidkarena tidak ada pengenal non-acak seperti alamat MAC dan apa pun yang ditentukan RFC4122? Jadi sebenarnya lebih acak.
Xeoncross
2
jawaban yang bagus; Saya telah mengembangkannya di stackoverflow.com/a/48134820/1122270 , dan saya pikir banyak orang sebenarnya tidak perlu menggunakan UUID secara khusus (atau sha1 / sha256 yang saya pikir perlu saya gunakan untuk random- saya sendiri- id masalah), tetapi hanya menginginkan sesuatu yang acak dan unik, dan sampel Anda memberikan awal yang baik untuk sebuah solusi
cnst
Terima kasih! Cukup sederhana
Karl Pokus
1. Ini tidak sesuai dengan standar apapun 2. Menggunakan hanya %xmemiliki masalah dengan nilai byte kurang dari 128, Anda perlu menerapkan padding, yaitu %04xuntuk pasangan byte
Ja͢ck
38

Ada implementasi resmi oleh Google: https://github.com/google/uuid

Menghasilkan UUID versi 4 berfungsi seperti ini:

package main

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

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

Coba di sini: https://play.golang.org/p/6YPi1djUMj9

shutefan
sumber
1
The godoc merekomendasikan menggunakan New()dan itu setara denganuuid.Must(uuid.NewRandom())
Jim
@Jim: kamu benar! Saya memperbarui jawaban saya sesuai.
shutefan
Perhatikan bahwa New () mungkin "fatal" (yang tidak masalah dalam beberapa kasus). Jika Anda tidak ingin program Anda menjadi fatal, gunakan saja uuid.NewRandom () - yang mengembalikan UUID dan kesalahan.
Tomer
@Tomer: benar! Meskipun saya bertanya-tanya dalam keadaan apa ini sebenarnya akan terjadi. Ini adalah bagian yang relevan dari kode: github.com/google/uuid/blob/… Secara default, pembaca adalah a rand.Reader. Saya tidak yakin apakah yang itu akan mengembalikan kesalahan atau apakah ini hanya dapat terjadi dengan Pembaca khusus ...
shutefan
1
Hai @ shutefan - Saya setuju bahwa ini mungkin jarang terjadi. rand.Reader memanggil fungsi Kernel ( golang.org/src/crypto/rand/rand.go ). Ini mungkin gagal dalam skenario tertentu.
Tomer
12

Dari postingan Russ Cox :

Tidak ada perpustakaan resmi. Mengabaikan pemeriksaan kesalahan, ini sepertinya akan bekerja dengan baik:

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

Catatan: Dalam versi asli, pra Go 1 baris pertama adalah:

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

Di sini ia mengkompilasi dan mengeksekusi, hanya /dev/urandommengembalikan semua nol di taman bermain. Seharusnya bekerja dengan baik secara lokal.

Di utas yang sama ada beberapa metode / referensi / paket lain yang ditemukan.

zzzz
sumber
12
Ini tidak akan menghasilkan UUID yang valid: UUID versi 4 (tipe berdasarkan data acak) memerlukan beberapa bit untuk disetel dengan cara tertentu untuk menghindari konflik dengan format UUID non-acak.
James Henstridge
4
Lebih baik digunakan import "crypto/rand"menurut saya, tetapi +1 untuk uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). Dikombinasikan dengan kode OP, dan ini berfungsi dengan baik.
chowey
2
Menggunakan paket crypto / rand: play.golang.org/p/7JJDx4GL77 . Kode zzzz melakukan apa yang dilakukan crypt / rand, kecuali kode itu juga mencakup platform yang tidak mendukung / dev / urandom (Windows).
Drew
Perlu dicatat bahwa ini adalah platform khusus
Dan Esparza
2
@Matt: masalahnya adalah format UUID lainnya mendapatkan keunikannya dengan mendelegasikannya ke otoritas lain (misalnya alamat MAC Ethernet Anda unik), dan kemudian menggabungkannya dengan yang lain (misalnya waktu plus penghitung). Jika Anda menghasilkan UUID acak yang tidak diformat dengan benar sebagai UU V4, Anda melemahkan sistem.
James Henstridge
8

Sebagai bagian dari spesifikasi uuid, jika Anda membuat sebuah uuid dari acak, itu harus berisi "4" sebagai karakter ke-13 dan "8", "9", "a", atau "b" di ke-17 ( sumber ).

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

The gorand paket memiliki metode UUID yang kembali Versi 4 (secara acak) UUID di representasi string kanonik nya ( "xxxxxxxxxxxx-xxxxxxxx-xxxxxxxxxxxx") dan itu RFC 4122 compliant.

Ia juga menggunakan paket crypto / rand untuk memastikan pembuatan UUID yang paling aman secara kriptografis di semua platform yang didukung oleh Go.

import "github.com/leonelquinteros/gorand"

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

    println(uuid)
} 
peiiion
sumber
4

Di Linux, Anda dapat membaca dari /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))
}

Tidak ada ketergantungan eksternal!

$ go run uuid.go 
3ee995e3-0c96-4e30-ac1e-f7f04fd03e44
Jiwa
sumber
4
Tidak dipilih karena secara langsung bergantung pada platform host dalam bahasa pemrograman yang digunakan untuk aplikasi multi-platform lebih buruk daripada ketergantungan eksternal.
Byebye
1
Bahasa pemrograman bisa multiplatform, tapi itu solusi yang sangat umum khusus Linux yang tidak akan pernah ada di platform lain, jadi, ini adalah respon yang valid IMO.
ton
1

Untuk Windows, saya baru-baru ini melakukan ini:

// +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.dll
sumber
2
Tidak dipilih karena secara langsung bergantung pada platform host dalam bahasa pemrograman yang digunakan untuk aplikasi multi-platform lebih buruk daripada ketergantungan eksternal.
Byebye
1
@ Byebye, saya bertanya-tanya mengapa Anda menganggap diri Anda otoritas untuk memutuskan apa yang "lebih buruk" (dan apa yang tidak) untuk membaca sepintas semua jawaban yang diberikan untuk pertanyaan ini dan downvote semua yang "bergantung pada sistem"? Jawaban-jawaban ini diberikan untuk a) memperluas cakrawala dari semua pilihan yang mungkin, dan b) secara kolektif menyajikan gambaran yang lengkap. Jadi tolong hentikan "bermain SO" kekanak-kanakan dan pikirkan beberapa hal sebelum Anda bertindak.
kostix
Jawaban singkat. Menulis kode yang dapat dipelihara. Jawaban Anda tidak dapat ditransfer ke platform lain. Jadi jika OP akan memilih untuk memindahkan aplikasi mereka ke platform lain, aplikasi tersebut akan rusak. Saya memiliki bagian yang adil dari orang-orang yang menulis kode yang bergantung pada platform di mana itu sama sekali tidak perlu dan itu menciptakan lebih banyak masalah daripada nilainya. Anda tidak menulis kode hanya untuk diri Anda sendiri. Anda menulis kode untuk orang-orang yang akan memeliharanya setelah Anda pergi. Karena itulah jawaban ini tidak tepat. Tidak ada alasan untuk menggunakan ad hominems dan memanggil saya kekanak-kanakan.
Byebye
1
@Byebye, saya bereaksi berlebihan, jadi mohon maaf atas serangannya. Aku masih tidak yakin dengan alasanmu, tapi seharusnya itu adalah kasus "mari setuju untuk tidak setuju".
kostix
1

Library ini adalah standar kami untuk pembuatan dan penguraian uuid:

https://github.com/pborman/uuid

James McGill
sumber
Perhatikan bahwa perpustakaan Google sendiri ( github.com/google/uuid ) sebagian didasarkan pada github.com/pborman/uuid , yang, pada gilirannya, telah memasukkan kembali beberapa perubahan yang dibuat Google. Namun, jika Anda ingin berkontribusi pada salah satu proyek ini, Anda harus menandatangani (atau telah menandatangani) Perjanjian Lisensi Kontributor (CLA). Ini tampaknya tidak terjadi pada Agustus 2015, ketika jawaban Anda ditambahkan; @pborman menambahkan bahwa hanya pada 16 Feb 2016 .
Gwyneth Llewelyn