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]하고 u[6].

UUID를 생성하는 더 좋은 방법이 있습니까?


1
대답 은 이제 더 적절 해 보입니다.
ViKiG

답변:


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

이 행은 바이트 6 및 8의 값을 특정 범위로 고정합니다. UUID에 대해 모두 유효한 값이 아닌 rand.Read범위의 임의의 바이트를 반환합니다 0-255. 내가 말할 수있는 한, 이것은 슬라이스의 모든 값에 대해 수행되어야합니다.

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 uuids 만 생성 할 수 있습니다.
Jay Taylor

12
그리고 nu7hatch / gouuid 라이브러리를 사용하여 172,488 uuids / 초를 생성 할 수있었습니다.
Jay Taylor

2
u[6]u[8]바이트에 대한 좋은 설명 .
chowey

3
내 시스템 (Ubuntu 15.10)에서는 줄 바꿈 문자를 제거하기 위해 strings.Trim (string (out))을 통해 명령 출력을 실행해야했습니다. 그렇지 않으면 후행? 파일 시스템의 문자.
gregtczap

39
존재하거나 존재하지 않을 수있는 외부 프로그램을 호출하는 것은 매우 간단한 작업을 수행하는 끔찍한 방법입니다.
Timmmm

96

go-uuid 라이브러리를 사용하여 UUID를 생성 할 수 있습니다 . 다음과 함께 설치할 수 있습니다.

go get github.com/nu7hatch/gouuid

다음을 사용하여 임의 (버전 4) UUID를 생성 할 수 있습니다.

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

반환 된 UUID유형은 16 바이트 배열이므로 이진 값을 쉽게 검색 할 수 있습니다. 또한 String()메서드 를 통해 표준 16 진 문자열 표현을 제공합니다 .

또한 코드는 유효한 버전 4 UUID를 생성하는 것처럼 보입니다. 마지막에 수행하는 비트 조작은 UUID의 버전 및 변형 필드를 설정하여 버전 4로 올바르게 식별합니다 . 이는 임의의 UUID를 다른 알고리즘 (예 : MAC 주소 및 시간을 기반으로 한 버전 1 UUID)을 통해 생성 된 것과 구별하기 위해 수행됩니다.


2
@Flimzy는 자신이 무엇을하고 있는지 모르는 사람들에게는 사실 일 가능성이 가장 높습니다. 불필요한 종속성을 도입하는 것은 항상 나쁜 일입니다.
Erik Aigner

31
@ErikAigner 50 줄이면 생각하고, 쓰고, 테스트 할 필요가 없습니다. 감사합니다.
RickyA 2015

3
이 라이브러리는 실제로 RFC4122를 준수하지 않는 것 같습니다. github.com/nu7hatch/gouuid/issues/28 (현재 발행 된 문제는 2016
Charles L.

1
@ErikAigner가 바퀴를 재창조하는 것도 불필요합니다. 도서관이 존재하고 그것을 잘 수행한다면, 그것을하는 방법을 배우기 위해 그것을하는 것 외에 왜 당신 자신을 귀찮게 하는가.
Sir

4
@ErikAigner 나는 단지 그 어리석은 것을 찾습니다. 당신이 더 잘할 수 없거나 당신의 프로그램에 특정한 것을 필요로하지 않는 한, 아무도 이미했던 것들을 재창조하지 않는다. 당신이 코드를 검사하고 그것이 잘 수행되는 것을 본다면 당신은 개발 시간과 비용을 낭비 할뿐만 아니라 당신이하고있는 일을 완전히 알지 못한다면 잠재적으로 버그 나 잘못된 구현을 가져올 수 있습니다. 이러한 라이브러리는 일반적으로 자신이하는 일을 아는 사람들로 만들어집니다. 타사 라이브러리를 사용하는 것은 신인이 아닙니다. 작동한다고 가정하고 코드를 먼저 검사하지 않는 유일한 신인입니다.
Sir

70

go-uuid라이브러리는 RFC4122에 호환되지 않습니다. 변형 비트가 올바르게 설정되지 않았습니다. 커뮤니티 구성원이이 문제를 수정하려고 여러 번 시도했지만 수정에 대한 풀 요청이 수락되지 않았습니다.

라이브러리를 기반으로 다시 작성한 Go uuid 라이브러리를 사용하여 UUID를 생성 할 수 있습니다 go-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

+1 동의, 업데이트 및 인쇄 / 포맷 확장이 이미 포함되어 있습니다.
eduncan911 2014 년

4
면책 조항이 없습니까? : p
chakrit 2014 년

3
"아래"도서관은 무엇입니까? 상당히 빠르게 변경 될 수 있으므로 위와 아래를 사용하지 않아야합니다.
Stephan Dollberg

또 다른 동등한 satori / go.uuid도 있습니다. 아직 시도하지 않았지만 nu7hatch 죽은 프로젝트 의 대체물로 사용할 것입니다 ...
shadyyx

52

"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_uuidMAC 주소 및 기타 RFC4122 지정과 같은 비 무작위 식별자가 누락 되었기 때문입니까? 따라서 실제로 무작위입니다.
Xeoncross

2
좋은 대답; 나는 stackoverflow.com/a/48134820/1122270 에서 확장했으며 실제로 많은 사람들이 UUID를 특별히 사용할 필요가 없다고 생각합니다 (내가 무작위로 사용해야한다고 생각한 sha1 / sha256도 아닙니다. id 문제),하지만 단순히 임의적이고 고유 한 것을 원하며 샘플은 솔루션을위한 좋은 시작을 제공합니다
cnst

감사! 간단한만큼
칼 Pokus

1. 이것은 어떤 표준도 준수하지 않습니다. 2. 사용하면 %x128보다 작은 바이트 값에 문제가 있습니다. 즉 %04x, 바이트 쌍에 대해 패딩을 적용해야합니다.
Ja͢ck

38

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


1
godoc는 사용을 권장 New()하고는 동등하다uuid.Must(uuid.NewRandom())

@Jim : 당신이 맞아요! 그에 따라 답변을 업데이트했습니다.
Shutefan

New ()는 "치명적"일 수 있습니다 (어떤 경우에는 괜찮습니다). 프로그램이 치명적이지 않게하려면 UUID와 오류를 반환하는 uuid.NewRandom ()을 사용하면됩니다.
Tomer

@Tomer : 사실! 어떤 상황에서 이것이 실제로 일어날 지 궁금합니다. 다음은 코드의 관련 부분입니다. github.com/google/uuid/blob/… 기본적으로 리더는 rand.Reader. 이것은 단지 사용자 정의 리더 ... 일어날 수 있는지 확실 하나는 적 오류를 반환하거나 것 아니에요 경우
shutefan

1
@shutefan 안녕하세요-드물다는 데 동의합니다. rand.Reader는 커널 함수 ( golang.org/src/crypto/rand/rand.go )를 호출합니다 . 특정 시나리오에서는 실패 할 수 있습니다.
Tomer 19


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하며 플레이 그라운드의 모든 0 만 반환합니다. 로컬에서 잘 작동합니다.

같은 스레드에는 몇 가지 다른 메서드 / 참조 / 패키지가 있습니다.


12
그래도 유효한 UUID가 생성되지는 않습니다. 버전 4 UUID (임의 데이터를 기반으로하는 유형)는 비 랜덤 UUID 형식과의 충돌을 피하기 위해 특정 방식으로 몇 비트를 설정해야합니다.
James Henstridge 2013

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:]). OP의 코드와 결합하면 훌륭하게 작동합니다.
chowey

2
crypto / rand 패키지 사용 : play.golang.org/p/7JJDx4GL77 . zzzz의 코드는 / dev / urandom (Windows)을 지원하지 않는 플랫폼을 포함한다는 점을 제외하고는 crypt / rand가하는 일을합니다.
Drew

이는 플랫폼에 따라 다릅니다
Dan Esparza

2
@Matt : 문제는 다른 UUID 형식이 다른 권한 (예 : 이더넷 MAC 주소가 고유함)에 위임 한 다음이를 다른 권한 (예 : 시간과 카운터)과 결합하여 고유성을 얻는다는 것입니다. V4로 올바르게 포맷되지 않은 임의의 UUID를 생성하면 시스템이 약화됩니다.
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

란드 패키지는 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입니다.
ton

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 2018-08-07

1
@Byebye, 나는 왜 당신이이 질문에 주어진 모든 대답을 훑어보고 "시스템 의존적"인 모든 것을 비하하기 위해 "더 나쁜"(그리고 그렇지 않은 것)을 결정할 권한이 있다고 생각 하는가? 이 답변은 a) 가능한 모든 선택의 지평을 넓히고 b) 전체 그림을 종합적으로 제시하기 위해 제공 되었습니다. 그러니 유치하게 "그렇게하는 것"을 그만두고 행동하기 전에 생각을 좀 넣어주세요.
kostix

짧은 답변. 유지 가능한 코드 작성. 답변을 다른 플랫폼으로 이식 할 수 없습니다. 따라서 OP가 애플리케이션을 다른 플랫폼으로 이동하도록 선택하면 애플리케이션이 중단됩니다. 나는 그것이 완전히 불필요하고 더 많은 문제를 일으키는 플랫폼 의존 코드를 작성한 사람들의 공정한 몫을 가지고 있습니다. 당신은 자신만을위한 코드를 작성하지 않습니다. 당신이 떠난 후에도 그것을 유지할 사람들을 위해 코드를 작성합니다. 이것이이 답변이 적절하지 않은 이유입니다. ad hominems에 의지하고 나를 유치하다고 부를 이유가 없습니다.
해지

1
@Byebye, 과잉 반응 했으니 공격에 대해 실례합니다. 나는 여전히 당신의 이유를 확신하지 못하지만, 아마도 그것은 "동의하지 않는 것에 동의하자"사건 일 것입니다.
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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.