有没有一种用 Go 语言生成 UUID 的方法?

我的代码是这样的:

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的字符串,但我不认为它是一个有效的 UUID。如果它是一个真正的 UUID,为什么它是一个 UUID,以及修改 u[8]u[6]值的代码的目的是什么?

有没有更好的生成 UUID 的方法?

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

这些行将字节6和8的值限制在一个特定的范围内。rand.Read返回范围 0-255中的随机字节,这些字节并非 UUID 的所有有效值。据我所知,应该对切片中的所有值执行此操作。

如果你在 Linux 上,你也可以调用 /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)
}

结果是:

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

来自拉斯 · 考克斯的 邮寄:

没有官方图书馆,无视错误检查, 这似乎行得通:

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

注意: 在原版中,pre Go 1版本的第一行是:

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

这里 它编译并执行,只有 /dev/urandom返回操场上的所有零。应该可以在本地工作。

在同一个线程中还发现了其他一些方法/引用/包。

对于 Windows,我最近这样做了:

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

您可以使用 Go-uid库生成 UUID:

go get github.com/nu7hatch/gouuid

您可以使用以下命令生成随机(版本4) UUID:

import "github.com/nu7hatch/gouuid"


...


u, err := uuid.NewV4()

返回的 UUID类型是一个16字节的数组,因此可以很容易地检索二进制值。它还通过其 String()方法提供标准的十六进制字符串表示。

您所使用的代码看起来也将生成一个有效的版本4 UUID: 最后执行的按位操作将 UUID 的版本和变体字段设置为正确的 标识为版本4。这样做是为了区分随机 UUID 和通过其他算法生成的 UUID (例如基于 MAC 地址和时间的第1版 UUID)。

go-uuid库与 RFC4122不兼容。变量位设置不正确。社区成员已经多次尝试修复这个问题,但是请求修复的请求没有被接受。

您可以使用我根据 go-uuid库重写的 Go uuid 库生成 UUID。有几个修复和改进。这个软件可以安装在:

go get github.com/twinj/uuid

您可以使用以下命令生成随机(版本4) UUID:

import "github.com/twinj/uuid"


u := uuid.NewV4()

返回的 UUID 类型是一个接口,基础类型是一个数组。

该库还生成 v1 UUID,并正确地生成 v3和 v5 UUID。有几种新的方法可以帮助打印和格式化,还有一些新的通用方法可以基于现有数据创建 UUID。

“ crypto/rand”是生成随机字节的跨平台 pkg

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
}

作为 uuid 规范的一部分,如果从随机生成 uuid,它必须包含第13个字符“4”和第17个字符(来源)中的“8”、“9”、“ a”或“ b”。

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

这个库是我们生成和解析 uuid 的标准:

Https://github.com/pborman/uuid

Gofrs/uuid Satori/go.uid的替代品,即 最受欢迎的 UUID 套件。它支持 UUID 版本1-5,并且与 RFC 4122和 DCE 1.1兼容。

import "github.com/gofrs/uuid"


// Create a Version 4 UUID, panicking on error
u := uuid.Must(uuid.NewV4())

Gorand包有一个 UUID 方法,它以规范字符串表示(“ xxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx”)返回 Version 4(随机生成) UUID,并且与 RFC4122兼容。

它还使用 crypto/rand 包来确保在 Go 支持的所有平台上以最安全的加密方式生成 UUID。

import "github.com/leonelquinteros/gorand"


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


println(uuid)
}

在 Linux 上,你可以阅读 /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))
}

没有外部依赖!

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

Google 有一个官方实现: https://github.com/google/uuid

生成版本4的 UUID 的工作原理如下:

package main


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


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

试试这里: https://play.golang.org/p/6YPi1djUMj9

所以你问:
U [8] = (u [8] | 0x80) & 0xBF//这是做什么的?
答: 本节定义了变体,你可以从 < a href = “ https://datatracer.ietf.org/doc/html/rfc4122 # section-4.1.1”rel = “ nofollow norefrer”> https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.1中了解更多

U [6] = (u [6] | 0x40) & 0x4F//这是做什么的?
答: 我们用最高4位显示版本号,所以在这个例子中是 4 so we want to set it with "0100"。版本4是使用最广泛的 UUID,它基于随机位生成。它使用128位,其中4位固定告诉版本号和 2 bits are fixed to tell variant。所以我们还剩下122位可以随机生成。< br >

您可以通过从 Google 导入包来生成 UUID v4:
Https://github.com/google/uuid

package main


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


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

同样 ,你可以试试我创建的软件包。它很轻,很容易理解

package main


import (
"fmt"
"github.com/bitactro/UUIDv4"
)


func main() {
    

fmt.Println(uuidv4.GenerateUUID4())
}

目前支持基于 RFC4122和 UUIDV4的 https://github.com/google/uuid模块。如果您想使用最新的 UUID 版本,如 UUID v7, 可以使用这个模块 https://github.com/uuid6/uuid6go-proto


UUID 版本7 : 一个全新的基于时间的 UUID 位布局,源自广泛实现和著名的 Unix Epoch 时间戳源。

unix_ts_ms|ver|rand_a|var|rand_b

因为 UUID V4包含随机值,并且不用于排序。但是,UUIDV7上有一个时间戳部分,它对于排序更加友好。


样本

    var gen uuid.UUIDv7Generator


uuid := gen.Next()
fmt.Println(uuid.ToString())
fmt.Println(uuid.Time())
fmt.Println(uuid.Timestamp())

输出

0632933765-357c-31b6-ed56-0daba726b1
2022-09-20 11:28:54 +0800 CST
1663644534

使用 google 软件包的随机 UUID:

package main


import (
"fmt"


"github.com/google/uuid"
)


func main() {
out := uuid.Must(uuid.NewRandom()).String()
fmt.Println(out)
}

产出:

a1c11a53-c4be-488f-89b6-f83bf2d48dab