有没有一种方法可以使用Go语言生成UUID?

199

我有这样的代码:

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,那么为什么要修改u [8]u [6]的值?这个代码有什么目的?
是否有更好的生成UUID的方法?

1
这个答案现在似乎更合适。 - ViKiG
15个回答

211

5
神文档(godoc)建议使用New(),其等效于uuid.Must(uuid.NewRandom()) - Jim
@Jim:你说得对!我已经相应地更新了我的答案。 - shutefan
3
请注意,New() 方法可能会出现“致命错误”(在某些情况下是可以接受的)。如果您不希望程序出现致命错误,请使用 uuid.NewRandom() 方法 - 它将返回一个 UUID 和一个错误。 - Tomer
1
嗨@shutefan - 我同意这可能很少见。rand.Reader调用内核函数(https://golang.org/src/crypto/rand/rand.go)。在某些情况下,这些函数可能会失败。 - Tomer
3
有一个 NewString。NewString 等同于表达式 uuid.New().String() - Ertuğrul Altınboğa
显示剩余2条评论

114
您可以使用go-uuid库生成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)。


3
对于那些不知道自己在做什么的人来说,@Flimzy 的说法很可能是正确的。引入不必要的依赖始终是一件坏事。 - user187676
42
只要这50行代码不需要我思考、编写和测试,我会接受它们的,谢谢你。我有其他事情要做,而不是重新发明轮子。 - RickyA
4
这个库看起来实际上不符合 RFC4122 标准:https://github.com/nu7hatch/gouuid/issues/28(截至2016年2月1日还是一个未解决的问题)。 - Charles L.
3
ErikAigner ,重新发明轮子也有点不必要。如果已经存在一个好用的库,为什么要自己写呢?除非你是为了学习而这么做。 - Sir
9
@ErikAigner,我认为这太荒谬了。除非你能做得更好或需要针对你的程序实现特定功能,否则没有人会重新发明已经完成的东西。如果你检查了代码并且发现它能很好地完成任务,为什么还要自己开发呢?这不仅浪费开发时间和成本,而且还可能引入漏洞,或者出现错误的实现方式,如果你不完全知道自己在做什么的话。这些库通常是由那些懂得如何使用它们的人创建的。使用第三方库并不是新手行为,只有不先检查代码就假设它能工作才是新手行为。 - Sir
显示剩余4条评论

79

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 的新通用方法。


4
我喜欢这个软件包。我已经正式采用它用于我所有的应用程序。我发现nu7hatch软件包不符合RFC4122标准。 - Richard Eng
4
免责声明缺失吗? :p - chakrit
4
"below" 是什么意思?在 Stack Overflow 上应避免使用 "above" 和 "below" 这样的词汇,因为它们的含义可能会随着时间而发生变化。 - Stephan Dollberg
1
@sadyyx satori/go.uuid 似乎在2020年6月已经半废弃了... 最后一次更新只是些表面修饰,而且还是在2018年中。是时候再次转换了! - Gwyneth Llewelyn
1
这现在依赖于一个作者似乎在2021年三月从GitHub上删除了的包。 - WilliamNHarvey
显示剩余2条评论

63

"crypto/rand" 是用于生成随机字节的跨平台软件包。

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_uuid”之所以被称为“假UUID”,是因为它缺少RFC4122规定的非随机标识符,例如MAC地址等。因此,它实际上比原本的UUID更加随机。 - Xeoncross
2
好的回答,我已经在https://stackoverflow.com/a/48134820/1122270上进行了扩展,我认为很多人实际上不需要特定的UUID(也不需要我自己用于我的random-id问题的sha1 / sha256),而只是想要一些随机和独一无二的东西,你的示例为解决方案提供了一个良好的开端。 - cnst
谢谢!相当简单。 - Karl Pokus
  1. 这不符合任何标准。
  2. 仅使用“%x”会存在字节值小于128的问题,您需要应用填充,即对于字节对使用“%04x”。
- Ja͢ck

34
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

27
值得注意的是,这种方法速度较慢;在2012年的MacBook Air上,此策略每秒只能生成170个UUID。 - Jay Taylor
12
使用nu7hatch/gouuid库,我能够每秒生成172,488个UUID。 - Jay Taylor
2
u[6]u[8] 字节的解释很清晰。 - chowey
3
在我的系统上(Ubuntu 15.10),我还需要通过运行命令output strings.Trim(string(out))来删除换行符,否则它会作为尾随的?字符输入到文件系统中。 - gregtzar
55
调用一个可能存在的外部程序来完成这个相当简单的任务是不好的方式。 - Timmmm
显示剩余3条评论

33

gofrs/uuidsatori/go.uuid 的替代品,后者是Go语言中最受欢迎的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())

12

来自Russ Cox的post:

没有官方库。忽略错误检查,这似乎可以很好地工作:

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

注意:在早期的 Go 1 版本中,第一行是:

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

这里可以编译和执行,只有在playground中/dev/urandom返回所有零。在本地应该可以正常工作。

在同一线程中还可以找到其他方法/参考/包。


15
这段内容的意思是:这个不会生成一个有效的UUID:版本4的UUID(基于随机数据的类型)需要将一些位以特定方式设置,以避免与非随机UUID格式发生冲突。 - James Henstridge
4
我认为最好使用import "crypto/rand",但是对于uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])给一个赞。与原作者的代码组合起来,这个方法非常适用。 - chowey
2
使用crypto/rand包:http://play.golang.org/p/7JJDx4GL77。zzzz的代码与crypt/rand相同,只是它还覆盖了不支持/dev/urandom(Windows)的平台。 - Drew
1
需要注意的是,这是特定于平台的。 - Dan Esparza
2
@Matt:问题在于其他UUID格式通过委托给其他机构(例如,您的以太网MAC地址是唯一的),然后将其与其他内容(例如时间加计数器)结合使用来获得其独特性。如果您生成的随机UUID格式不正确,则会削弱系统。 - James Henstridge
显示剩余3条评论

10

使用谷歌的软件包生成随机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

对于那些不想使用上面接受的解决方案中提到的操作系统函数的人来说。 - SillyMaxwell

8
作为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 

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

10
因为在用于多平台应用程序的编程语言中直接依赖主机平台比外部依赖更糟糕,所以被踩。 - Byebye
2
编程语言可以是跨平台的,但很常见的解决方案是针对Linux特定的,这些解决方案永远不会出现在其他平台上,所以,在我看来这是一个有效的回答。 - ton
这个符合RFC 4122标准吗?它是哪个版本的UUID? - xuiqzy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接