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


109

我有看起来像这样的代码:

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]and 的值的代码的目的是什么u[6]

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


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

Answers:


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

这些行将字节6和8的值限制在特定范围内。rand.Read返回range内的随机字节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

23
值得注意的是,这种方法很慢。在2012年的MacBook Air上,此策略只能产生170 uuid /秒。
杰伊·泰勒

12
使用nu7hatch / gouuid库,我能够每秒生成172488 uuid。
杰伊·泰勒

2
u[6]u[8]字节的好解释。
chowey 2014年

3
在我的系统(Ubuntu 15.10)上,我还需要通过strings.Trim(string(out))运行命令输出以删除换行符,否则将其作为结尾输入?文件系统中的字符。
gregtczap '16

39
调用可能不存在的外部程序是执行此相当简单的任务的糟糕方法。
Timmmm '16

96

您可以使用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)。


2
@Flimzy对于不知道自己在做什么的人来说很可能是正确的。引入不必要的依赖关系总是一件坏事。
埃里克·艾格纳2015年

31
@ErikAigner只要是50行,我就不必思考,编写和测试,我会感谢他们。.我还有其他事情要做,然后重新发明轮子。
RickyA 2015年

3
该库看起来实际上不符合RFC4122:github.com/nu7hatch/gouuid/issues/28(截至2016
Charles L.

1
@ErikAigner重塑车轮也是不必要的。如果一个图书馆存在并且做得很好,那为什么不去做自己的事,而不是自己去学习如何做。
爵士

4
@ErikAigner我只是觉得很有趣。除非您可以做得更好或者需要特定于程序的东西,否则没有人会重做已经完成的工作,如果您检查代码并看到它能很好地完成工作,那么您自己就麻烦了-不仅浪费开发时间和成本,您还如果您不完全了解自己在做什么,可能会带来错误或简单的错误实现,这些库通常使确实知道自己在做什么的人。它不是菜鸟使用第三方库,其唯一的新秀只是假设它的工作原理,而不是首先检查代码..
爵士

70

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和5 UUID。有几种帮助打印和格式化的新方法,还有一些新的通用方法可以根据现有数据创建UUID。


4
我喜欢这个包裹。我已经为所有应用程序正式采用了它。我发现nu7hatch软件包不符合RFC4122。
Richard Eng 2014年

+1同意,更新和打印/格式扩展已包含在内。
eduncan911

4
缺少免责声明?:p
chakrit

3
什么是“下方”图书馆?您应该避免在SO上下使用,因为这可能会很快改变。
Stephan Dollberg,2015年

还有另一个等效的satori / go.uuid。还没有尝试过,但是我将用它来代替nu7hatch死项目...
shadyyx 16-4-7

52

“ 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
}

3
pseudo_uuid因为它缺少诸如MAC地址之类的非随机标识符以及其他指定的RFC4122?因此,它实际上更多的是随机的。
Xeoncross

2
好答案; 我已经在stackoverflow.com/a/48134820/1122270上进行了扩展,我认为很多人实际上不需要专门使用UUID(也不需要我认为自己需要使用sha1 / sha256( id问题),但只需要一些随机且独特的内容,而您的示例就为解决方案提供了一个良好的开端
cnst 18'Jan

谢谢!很简单
Karl Pokus

1.这不符合任何标准2.使用仅%x存在字节值小于128的问题,您需要应用填充,即%04x对于一个字节对
–Ja͢ck,

38

谷歌有一个官方的实现: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


1
godoc建议使用New(),它等同于uuid.Must(uuid.NewRandom())
吉姆

@吉姆:你是对的!我相应地更新了我的答案。
shutefan

请注意,New()可能“致命”(在某些情况下可以)。如果您不想让程序致命,请使用uuid.NewRandom()-返回UUID和错误。
Tomer,

@Tomer:是的!尽管我不知道在哪种情况下会发生这种情况。这是代码的相关部分:github.com/google/uuid/blob/…默认情况下,阅读器为rand.Reader。我不确定是不是会返回错误,还是只能使用自定义阅读器来发生...
shutefan

1
嗨@shutefan-我同意这可能很少见。rand.Reader调用内核函数(golang.org/src/crypto/rand/rand.go)。在某些情况下,这些操作可能会失败。
Tomer,


12

从Russ Cox的帖子中

没有官方图书馆。忽略错误检查,这似乎可以正常工作:

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)

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

在同一线程中,找到了一些其他方法/引用/包。


12
但是,这不会生成有效的UUID:版本4 UUID(基于随机数据的类型)需要以某种方式设置一些位,以避免与非随机UUID格式冲突。
James Henstridge

4
import "crypto/rand"我认为最好使用,但对+1 uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])。与OP的代码结合使用,效果很好。
chowey 2014年

2
使用crypto / rand软件包:play.golang.org/p/7JJDx4GL77。zzzz的代码执行crypt / rand的操作,除了它还涵盖了不支持/ dev / urandom的平台(Windows)。
Drew

应当注意,这是特定
Dan Esparza,2016年

2
@Matt:问题在于,其他UUID格式通过委派给其他权威机构(例如,您的以太网MAC地址是唯一的)来获得其唯一性,然后将其与其他内容(例如时间和计数器)组合在一起。如果生成的随机UUID格式未正确设置为V4,则会削弱系统性能。
James Henstridge

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 

4

gorand封装具有UUID方法,它返回一个版本4(随机生成的)UUID在其规范的字符串表示(“XXXXXXXXXXXX-XXXXXXXX-XXXXXXXXXXXX”)和它的RFC 4122兼容。

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

import "github.com/leonelquinteros/gorand"

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

    println(uuid)
} 

4

在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

4
之所以投票,是因为直接依赖于用于多平台应用程序的编程语言中的宿主平台要比外部依赖性差。
Byebye

1
编程语言可以是多平台的,但是它是特定于Linux的非常常见的解决方案,永远不会在其他平台上使用,因此,这是IMO的有效响应。

1

对于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
}

2
之所以投票,是因为直接依赖于用于多平台应用程序的编程语言中的宿主平台要比外部依赖性差。
Byebye

1
@Byebye,我想知道为什么您认为自己有权决定哪些“更糟”(哪些不是)跳过该问题的所有答案并否决所有“与系统有关”的答案?提供这些答案是为了a)拓宽所有可能的选择范围,b)共同呈现出完整的画面。因此,请停止幼稚地“玩游戏”,并在行动前思考一下。
kostix

简短的答案。编写可维护的代码。您的答案无法移植到其他平台。因此,如果OP选择将其应用程序移动到另一个平台,则该应用程序将中断。我有很多人在编写平台相关的代码,这些代码完全没有必要,而且会带来更多的麻烦,而不是值得的。您不只是为自己编写代码。您为离开后将维护它的人员编写代码。这就是为什么这个答案不合适。没有理由求情,称我为幼稚。
Byebye

1
@再见,我反应过度,所以请原谅我发动袭击。我仍然不确定您的原因,但据推测是“我们同意不同意”的情况。
kostix

1

该库是我们用于uuid生成和解析的标准:

https://github.com/pborman/uuid


请注意,Google自己的库(github.com/google/uuid)部分基于github.com/pborman/uuid,而该库又合并了Google所做的一些更改。但是,据称,如果您希望为这两个项目中的任何一个做出贡献,则需要签署(或已经签署)贡献者许可协议(CLA)。显然,2015年8月情况并非如此。@pborman补充说,仅在2016年2月16日
Gwyneth Llewelyn
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.