Có phương pháp nào để tạo UUID với ngôn ngữ go không


109

Tôi có mã trông như thế này:

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)

Nó trả về một chuỗi có độ dài 32, nhưng tôi không nghĩ đó là một UUID hợp lệ. Nếu đó là UUID thực, tại sao nó là UUID và mục đích của mã sửa đổi giá trị của u[8]và là gì u[6].

Có cách nào tốt hơn để tạo UUID không?


1
Câu trả lời này có vẻ phù hợp hơn bây giờ.
ViKiG

Câu trả lời:


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

Các dòng này kẹp các giá trị của byte 6 và 8 vào một phạm vi cụ thể. rand.Readtrả về các byte ngẫu nhiên trong phạm vi 0-255, không phải là tất cả các giá trị hợp lệ cho một UUID. Theo như tôi có thể nói, điều này nên được thực hiện cho tất cả các giá trị trong lát cắt.

Nếu bạn đang sử dụng Linux, bạn có thể gọi điện theo cách khác /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)
}

Kết quả là:

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

23
Đáng chú ý, cách tiếp cận này là chậm; trên MacBook Air 2012, chiến lược này chỉ có thể tạo ra 170 uuids / giây.
Jay Taylor

12
Và bằng cách sử dụng thư viện nu7hatch / gouuid, tôi có thể tạo 172.488 uuid / giây.
Jay Taylor,

2
Giải thích tốt về các byte u[6]u[8].
chowey

3
Trên hệ thống của tôi (Ubuntu 15.10), tôi cũng cần chạy lệnh đầu ra thông qua string.Trim (string (out)) để loại bỏ ký tự dòng mới, nếu không, nó đã được nhập vào dưới dạng dấu? ký tự trong hệ thống tệp.
gregtczap

39
Gọi một chương trình bên ngoài có thể tồn tại hoặc không tồn tại là một cách tồi tệ để thực hiện tác vụ khá đơn giản này.
Timmmm

96

Bạn có thể tạo UUID bằng thư viện go-uuid . Điều này có thể được cài đặt với:

go get github.com/nu7hatch/gouuid

Bạn có thể tạo UUID ngẫu nhiên (phiên bản 4) với:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

Kiểu trả về UUIDlà một mảng 16 byte, vì vậy bạn có thể lấy giá trị nhị phân một cách dễ dàng. Nó cũng cung cấp biểu diễn chuỗi hex tiêu chuẩn thông qua String()phương thức của nó .

Mã bạn có trông giống như nó cũng sẽ tạo ra một UUID phiên bản 4 hợp lệ: thao tác bitwise mà bạn thực hiện ở cuối đặt các trường phiên bản và biến thể của UUID thành chính xác định xác nó là phiên bản 4 . Điều này được thực hiện để phân biệt các UUID ngẫu nhiên với các UUID được tạo thông qua các thuật toán khác (ví dụ: UUID phiên bản 1 dựa trên địa chỉ MAC và thời gian của bạn).


2
@Flimzy dành cho những người không biết họ đang làm điều đó rất có thể đúng. Giới thiệu các phụ thuộc không cần thiết luôn là một điều xấu.
Erik Aigner

31
@ErikAigner Miễn là 50 dòng, tôi không cần phải suy nghĩ, viết và kiểm tra, tôi sẽ nhận chúng, cảm ơn bạn .. Tôi có những việc khác để làm sau đó phát minh lại bánh xe.
RickyA

3
Thư viện này có vẻ như không thực sự tuân thủ RFC4122: github.com/nu7hatch/gouuid/issues/28 (sự cố hiện đang mở kể từ
Charles L.

1
@ErikAigner phát minh lại bánh xe cũng hơi không cần thiết. Nếu một thư viện tồn tại và nó hoạt động tốt, tại sao bạn phải làm việc của riêng bạn khác hơn là nếu bạn đang làm nó để học cách làm điều đó.
Thưa ngài,

4
@ErikAigner Tôi chỉ tìm thấy riduclous đó. Không ai phát minh lại những thứ đã hoàn thành trừ khi bạn có thể làm tốt hơn hoặc cần một thứ gì đó cụ thể cho chương trình của mình, nếu bạn kiểm tra mã và thấy nó hoạt động tốt, tại sao bạn phải tự mình làm điều đó - bạn không chỉ lãng phí thời gian và chi phí phát triển mà còn có khả năng mang lại lỗi hoặc đơn giản là triển khai sai nếu bạn hoàn toàn không biết mình đang làm gì, những thư viện này thường được tạo ra cho những người biết họ đang làm gì. Nó không phải là tân binh để sử dụng các thư viện của bên thứ ba, tân binh duy nhất của nó chỉ cho rằng nó hoạt động và không kiểm tra mã trước ..
Sir

70

Các go-uuid thư viện là KHÔNG RFC4122 tuân thủ. Các bit biến thể không được đặt chính xác. Các thành viên cộng đồng đã cố gắng sửa lỗi này một vài lần nhưng các yêu cầu kéo về bản sửa lỗi không được chấp nhận.

Bạn có thể tạo UUID bằng thư viện Go uuid mà tôi đã viết lại dựa trên go-uuidthư viện. Có một số bản sửa lỗi và cải tiến. Điều này có thể được cài đặt với:

go get github.com/twinj/uuid

Bạn có thể tạo UUID ngẫu nhiên (phiên bản 4) với:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Kiểu UUID trả về là một giao diện và kiểu cơ bản là một mảng.

Thư viện cũng tạo các UUID v1 và tạo các UUID v3 và 5 một cách chính xác. Có một số phương pháp mới giúp in và định dạng cũng như các phương pháp chung mới để tạo UUID dựa trên dữ liệu hiện có.


4
Tôi thích gói này. Tôi đã chính thức áp dụng nó cho tất cả các ứng dụng của mình. Tôi thấy rằng gói nu7hatch không tuân thủ RFC4122.
Richard Eng

+1 Đồng ý, các bản cập nhật và tiện ích mở rộng in / định dạng đã được bao gồm.
eduncan911

4
Tuyên bố thiếu trách nhiệm? : p
chakrit

3
Thư viện "bên dưới" là gì? Bạn nên tránh sử dụng trên và dưới trên SO vì điều đó có thể thay đổi khá nhanh.
Stephan Dollberg

Ngoài ra còn có một tương đương khác, satori / go.uuid . Không thử nó chưa nhưng tôi sẽ sử dụng nó như là một sự thay thế của nu7hatch dự án chết ...
shadyyx

52

"crypto / rand" là pkg nền tảng chéo cho phát sinh byte ngẫu nhiên

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_uuidbởi vì nó thiếu các định danh không ngẫu nhiên như địa chỉ MAC và bất cứ điều gì khác mà RFC4122 chỉ định? Vì vậy, nó thực sự ngẫu nhiên hơn .
Xeoncross

2
câu trả lời tốt; Tôi đã mở rộng nó tại stackoverflow.com/a/48134820/1122270 và tôi nghĩ rằng nhiều người thực sự không cần sử dụng UUID cụ thể (cũng như sha1 / sha256 mà tôi nghĩ rằng tôi cần sử dụng cho ngẫu nhiên của mình- id vấn đề), nhưng chỉ đơn giản là muốn một cái gì đó ngẫu nhiên và độc đáo, và mẫu của bạn cung cấp một khởi đầu tốt cho một giải pháp
cnst 07-01

Cảm ơn! Đủ đơn giản
Karl Pokus

1. Điều này không tuân theo bất kỳ tiêu chuẩn nào 2. Việc sử dụng chỉ %xgặp vấn đề với giá trị byte nhỏ hơn 128, bạn cần áp dụng đệm, tức là %04xcho một cặp byte
Ja͢ck

38

Có một triển khai chính thức của Google: https://github.com/google/uuid

Tạo UUID phiên bản 4 hoạt động như sau:

package main

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

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

Hãy thử nó tại đây: https://play.golang.org/p/6YPi1djUMj9


1
Các godoc khuyến cáo sử dụng New()và nó tương đương vớiuuid.Must(uuid.NewRandom())
Jim

@Jim: bạn nói đúng! Tôi đã cập nhật câu trả lời của mình cho phù hợp.
shutefan

Lưu ý rằng New () có thể "gây tử vong" (điều này không sao trong một số trường hợp). Trong trường hợp bạn không muốn chương trình của mình nguy hiểm, chỉ cần sử dụng uuid.NewRandom () - trả về một UUID và một lỗi.
Tomer

@ Khách hàng: đúng! Mặc dù tôi tự hỏi điều này sẽ thực sự xảy ra trong hoàn cảnh nào. Đây là phần có liên quan của mã: github.com/google/uuid/blob/… Theo mặc định, trình đọc là a rand.Reader. Tôi không chắc liệu cái đó có bao giờ trả lại lỗi hay không hay điều này chỉ có thể xảy ra với Trình đọc tùy chỉnh ...
shutefan

1
Xin chào @shutefan - Tôi đồng ý rằng nó có thể hiếm. rand.Reader gọi các hàm Kernel ( golang.org/src/crypto/rand/rand.go ). Chúng có thể không thành công trong một số trường hợp nhất định.
Tomer


12

Từ bài đăng của Russ Cox :

Không có thư viện chính thức. Bỏ qua kiểm tra lỗi, điều này có vẻ như nó sẽ hoạt động tốt:

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

Lưu ý: Trong bản gốc, trước phiên bản Go 1, dòng đầu tiên là:

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

Đây nó biên dịch và thực thi, chỉ/dev/urandom trả về tất cả các số không trong sân chơi. Nên hoạt động tốt tại địa phương.

Trong cùng một chủ đề, có một số phương thức / tham chiếu / gói khác được tìm thấy.


12
Tuy nhiên, điều này sẽ không tạo ra một UUID hợp lệ: UUID phiên bản 4 (loại dựa trên dữ liệu ngẫu nhiên) yêu cầu một vài bit được đặt theo một cách nhất định để tránh xung đột với các định dạng UUID không ngẫu nhiên.
James Henstridge

4
import "crypto/rand"Theo ý kiến ​​của tôi, tốt hơn nên sử dụng , nhưng +1 cho uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). Kết hợp với mã của OP, và điều này hoạt động tuyệt vời.
chowey

2
Sử dụng gói tiền điện tử / rand: play.golang.org/p/7JJDx4GL77 . Mã của zzzz thực hiện những gì crypt / rand, ngoại trừ việc nó cũng bao gồm các nền tảng không hỗ trợ / dev / urandom (Windows).
Phi hành đoàn

Cần lưu ý rằng đây là nền tảng cụ thể
Dan Esparza.

2
@Matt: vấn đề là các định dạng UUID khác có được tính duy nhất của chúng bằng cách ủy quyền cho một số cơ quan khác (ví dụ: địa chỉ MAC Ethernet của bạn là duy nhất), và sau đó kết hợp nó với một cái gì đó khác (ví dụ thời gian cộng với bộ đếm). Nếu bạn tạo một UUID ngẫu nhiên không được định dạng đúng như một UUID V4, bạn đang làm suy yếu hệ thống.
James Henstridge

8

Là một phần của thông số kỹ thuật uuid, nếu bạn tạo uuid từ ngẫu nhiên, nó phải chứa "4" là ký tự thứ 13 và "8", "9", "a" hoặc "b" ở ký tự thứ 17 ( nguồn ).

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

Các gorand gói có một phương pháp UUID rằng lợi nhuận ở phiên bản 4 (được tạo ngẫu nhiên) UUID trong chuỗi đại diện kinh điển của nó ( "xxxxxxxxxxxx-xxxxxxxx-xxxxxxxxxxxx") và nó RFC 4122 tuân thủ.

Nó cũng sử dụng gói tiền điện tử / rand để đảm bảo tạo ra UUID được bảo mật bằng mật mã trên tất cả các nền tảng được Go hỗ trợ.

import "github.com/leonelquinteros/gorand"

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

    println(uuid)
} 

4

Trên Linux, bạn có thể đọc từ /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))
}

Không phụ thuộc bên ngoài!

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

4
Bị phản đối vì phụ thuộc trực tiếp vào nền tảng máy chủ lưu trữ trong ngôn ngữ lập trình được sử dụng cho các ứng dụng đa nền tảng kém hơn sự phụ thuộc bên ngoài.
Tạm biệt

1
Ngôn ngữ lập trình có thể đa dạng, nhưng các giải pháp rất phổ biến dành riêng cho Linux sẽ không bao giờ có trên nền tảng khác, vì vậy, đó là một IMO phản hồi hợp lệ.
tấn

1

Đối với Windows, gần đây tôi đã làm điều này:

// +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
Bị phản đối vì phụ thuộc trực tiếp vào nền tảng máy chủ lưu trữ trong ngôn ngữ lập trình được sử dụng cho các ứng dụng đa nền tảng kém hơn sự phụ thuộc bên ngoài.
Tạm biệt

1
@ Tạm biệt, tôi tự hỏi tại sao bạn lại tự cho mình là người có thẩm quyền quyết định điều gì "tệ hơn" (và điều gì không) khi đọc lướt qua tất cả các câu trả lời cho câu hỏi này và phản đối tất cả những câu "phụ thuộc vào hệ thống"? Những câu trả lời này được đưa ra để a) mở rộng chân trời của tất cả các lựa chọn có thể có, và b) trình bày chung một bức tranh hoàn chỉnh. Vì vậy, xin hãy ngừng "chơi SO" một cách trẻ con và suy nghĩ trước khi hành động.
kostix

Câu trả lời ngắn. Viết mã có thể bảo trì. Câu trả lời của bạn không thể được chuyển sang một nền tảng khác. Vì vậy, nếu OP chọn chuyển ứng dụng của họ sang một nền tảng khác, ứng dụng sẽ bị hỏng. Tôi đã chia sẻ công bằng của mình về những người đã viết mã phụ thuộc vào nền tảng, nơi nó hoàn toàn không cần thiết và nó tạo ra nhiều rắc rối hơn thì nó đáng giá. Bạn không viết mã cho riêng mình. Bạn viết mã cho những người sẽ duy trì nó sau khi bạn ra đi. Đó là lý do tại sao câu trả lời này không phù hợp. Không có lý do gì để sử dụng các bài học quảng cáo và gọi tôi là trẻ con.
Tạm biệt

1
@ Tạm biệt, tôi đã phản ứng quá mức, vì vậy xin phép tôi tấn công. Tôi vẫn chưa thuyết phục lý do của bạn, nhưng được cho là trường hợp "đồng ý không đồng ý".
kostix

1

Thư viện này là tiêu chuẩn của chúng tôi để tạo và phân tích cú pháp uuid:

https://github.com/pborman/uuid


Lưu ý rằng thư viện riêng của Google ( github.com/google/uuid ) một phần dựa trên github.com/pborman/uuid , do đó, đã kết hợp lại một số thay đổi mà Google thực hiện. Tuy nhiên, theo cáo buộc, nếu bạn muốn đóng góp cho một trong hai dự án này, bạn cần phải ký (hoặc đã ký) Thỏa thuận cấp phép cộng tác viên (CLA). Điều này dường như đã không xảy ra vào tháng 8 năm 2015, khi câu trả lời của bạn được thêm vào; @pborman đã thêm rằng chỉ vào ngày 16 tháng 2 năm 2016 .
Gwyneth Llewelyn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.