Есть ли способ сгенерировать UUID с языком go

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?

HardPass
источник
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 определенным диапазоном. 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
jimt
источник
23
Примечательно, что этот подход медленный; на MacBook Air 2012 года эта стратегия может выдавать только 170 единиц в секунду.
Джей Тейлор
12
А с помощью библиотеки nu7hatch / gouuid я смог сгенерировать 172488 uuid в секунду.
Джей Тейлор,
2
Хорошее объяснение из u[6]и u[8]байтов.
chowey
3
В моей системе (Ubuntu 15.10) мне также нужно было запустить вывод команды через strings.Trim (string (out)), чтобы удалить символ новой строки, иначе он был введен как завершающий? символ в файловой системе.
gregtczap
39
Вызов внешней программы, которая может существовать, а может и не существовать, - ужасный способ выполнить эту довольно простую задачу.
Timmmm
96

Вы можете сгенерировать UUID с помощью библиотеки go-uuid . Это может быть установлено с помощью:

go get github.com/nu7hatch/gouuid

Вы можете генерировать случайные (версия 4) UUID с помощью:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

Возвращаемый UUIDтип - это 16-байтовый массив, поэтому вы можете легко получить двоичное значение. Он также предоставляет стандартное шестнадцатеричное строковое представление через свой String()метод.

Код, который у вас есть, также выглядит так, как будто он также сгенерирует действительный UUID версии 4: побитовое манипулирование, которое вы выполняете в конце, устанавливает поля версии и варианта UUID, чтобы правильно идентифицировать его как версию 4 . Это сделано для того, чтобы отличать случайные UUID от UUID, сгенерированных с помощью других алгоритмов (например, UUID версии 1 на основе вашего MAC-адреса и времени).

Джеймс Хенстридж
источник
2
@Flimzy для людей, которые не знают, что делают, это, скорее всего, правда. Вводить ненужные зависимости - всегда плохо.
Эрик Айгнер
31
@ErikAigner Пока это 50 строк, о которых мне не нужно думать, писать и тестировать, я возьму их, спасибо .. У меня есть другие дела, а затем изобретать велосипед.
RickyA
3
Эта библиотека выглядит так, как будто она на самом деле не соответствует RFC4122: github.com/nu7hatch/gouuid/issues/28 (в настоящее время открытый вопрос по состоянию на 01.02.2016)
Чарльз Л.
1
@ErikAigner изобретать велосипед тоже не нужно. Если библиотека существует и делает это хорошо, зачем делать свои собственные, кроме как если вы делаете это, чтобы узнать, как это сделать.
Сэр
4
@ErikAigner, я нахожу это неприятным. Никто не изобретает то, что уже сделано, если только вы не можете сделать лучше или вам не нужно что-то конкретное для своей программы; если вы проверяете код и видите, что он работает хорошо, зачем беспокоиться о том, чтобы делать это самостоятельно - вы не только тратите время и деньги на разработку, но и потенциально могут привести к ошибкам или просто неправильным реализациям, если вы не полностью знаете, что делаете, эти библиотеки обычно создаются людьми, которые действительно знают, что они делают. Использование сторонних библиотек - не новичок, его единственный новичок просто предполагает, что это работает, и не проверяет код сначала ...
Сэр
70

go-uuidБиблиотека НЕ RFC4122 совместимый. Биты варианта установлены неправильно. Члены сообщества предпринимали несколько попыток исправить это, но запросы на исправление не принимаются.

Вы можете сгенерировать UUID с помощью библиотеки Go uuid, которую я переписал на основе go-uuidбиблиотеки. Есть несколько исправлений и улучшений. Это может быть установлено с помощью:

go get github.com/twinj/uuid

Вы можете генерировать случайные (версия 4) UUID с помощью:

import "github.com/twinj/uuid"

u := uuid.NewV4()

Возвращаемый тип UUID - это интерфейс, а базовый тип - это массив.

Библиотека также генерирует UUID v1 и правильно генерирует UUID v3 и 5. Есть несколько новых методов, помогающих с печатью и форматированием, а также новые общие методы для создания UUID на основе существующих данных.

twinj
источник
4
Мне нравится этот пакет. Я официально применил его для всех своих приложений. Я обнаружил, что пакет nu7hatch не соответствует RFC4122.
Ричард Энг
+1 Согласен, обновления и расширения для печати / форматирования уже включены.
eduncan911
4
Заявление об отказе от ответственности отсутствует? : p
чакрит
3
Что за библиотека «внизу»? Вам следует избегать использования выше и ниже в SO, так как это может измениться довольно быстро.
Стефан Доллберг
Есть еще один эквивалент, 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_uuidпотому что в нем отсутствуют неслучайные идентификаторы, такие как MAC-адрес и все, что еще указано в RFC4122? Так что на самом деле это более случайно.
Xeoncross
2
хороший ответ; Я расширил его на stackoverflow.com/a/48134820/1122270 , и я думаю, что многим людям на самом деле не нужно специально использовать UUID (ни sha1 / sha256, который, как я думал, мне нужно использовать для своих собственных случайных- id), но просто хотите чего-то случайного и уникального, и ваш образец обеспечивает хорошее начало для решения
cnst
Спасибо! Достаточно просто
Карл Покус
1. Это не соответствует ни одному стандарту. 2. При использовании просто %xвозникают проблемы со значениями байтов меньше 128, вам нужно применить заполнение, то есть %04xдля пары байтов
Як
38

Есть официальная реализация Google: https://github.com/google/uuid

Генерация UUID версии 4 работает следующим образом:

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())
Джим
@ Джим: ты прав! Я соответственно обновил свой ответ.
шутефан
Обратите внимание, что New () может быть «фатальным» (что в некоторых случаях нормально). Если вы не хотите, чтобы ваша программа была фатальной, просто используйте uuid.NewRandom (), которая возвращает UUID и ошибку.
Tomer
@Tomer: правда! Хотя мне интересно, при каких обстоятельствах это могло бы произойти. Это соответствующая часть кода: github.com/google/uuid/blob/… По умолчанию читателем является файл rand.Reader. Я не уверен, что это когда-нибудь вернет ошибку, или это может произойти только с пользовательским Reader ...
shutefan
1
Привет @shutefan - Я согласен, что это может быть редкостью. rand.Reader вызывает функции ядра ( golang.org/src/crypto/rand/rand.go ). В определенных сценариях они могут не работать.
Tomer
12

От Russ Кокса пост :

Официальной библиотеки нет. Игнорируя проверку ошибок, похоже, что все будет нормально:

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возвращает только все нули на игровой площадке. Должно работать нормально локально.

В этом же потоке найдены некоторые другие методы / ссылки / пакеты.

zzzz
источник
12
Однако это не сгенерирует действительный UUID: UUID версии 4 (тип, основанный на случайных данных) требует, чтобы несколько битов были установлены определенным образом, чтобы избежать конфликта с неслучайными форматами UUID.
Джеймс Хенстридж
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
2
Использование пакета crypto / rand: play.golang.org/p/7JJDx4GL77 . Код zzzz делает то же, что и crypt / rand, за исключением того, что он также охватывает платформы, не поддерживающие / dev / urandom (Windows).
Дрю
Следует отметить, что это зависит от платформы
Дэн Эспарза
2
@Matt: проблема в том, что другие форматы UUID получают свою уникальность, делегируя какой-то другой орган (например, что ваш MAC-адрес Ethernet уникален), а затем комбинируя это с чем-то еще (например, временем плюс счетчик). Если вы создаете случайный UUID, который неправильно отформатирован как V4, вы ослабляете систему.
Джеймс Хенстридж
8

Как часть спецификации uuid, если вы генерируете uuid из случайного числа, он должен содержать «4» в качестве 13-го символа и «8», «9», «a» или «b» в 17-м ( исходном ).

// 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, который возвращает UUID версии 4 (сгенерированный случайным образом) в его каноническом строковом представлении («xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx») и соответствует RFC 4122.

Он также использует пакет crypto / rand для обеспечения наиболее криптографически безопасного создания UUID на всех платформах, поддерживаемых Go.

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 07
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 07
1
@Byebye, мне интересно, почему вы считаете себя властью решать, что «хуже» (а что нет), просматривать все ответы, данные на этот вопрос, и голосовать против всех, которые «зависят от системы»? Эти ответы были даны, чтобы: а) расширить кругозор для всех возможных выборов и б) в совокупности представить полную картину. Так что, пожалуйста, прекратите по-детски «так играть» и подумайте, прежде чем действовать.
kostix 07
Короткий ответ. Написание поддерживаемого кода. Ваш ответ нельзя перенести на другую платформу. Так что, если ОП решит переместить свое приложение на другую платформу, приложение сломается. У меня была немалая доля людей, которые писали код, зависящий от платформы, где он совершенно не нужен и создает больше проблем, чем того стоит. Вы не пишете код только для себя. Вы пишете код для людей, которые будут его поддерживать после вашего ухода. Вот почему этот ответ не подходит. Нет причин прибегать к ad hominems и называть меня ребячливым.
Byebye 07
1
@ Прощай, я слишком остро отреагировал, так что, пожалуйста, извини меня за атаку. Я все еще не уверен в ваших причинах, но, предположительно, дело в том, что "давайте соглашаться не соглашаться".
kostix 07
1

Эта библиотека является нашим стандартом для генерации и анализа uuid:

https://github.com/pborman/uuid

Джеймс МакГилл
источник
Обратите внимание, что собственная библиотека Google ( github.com/google/uuid ) частично основана на github.com/pborman/uuid , который, в свою очередь, включил некоторые изменения, внесенные Google. Однако, предположительно, если вы хотите внести свой вклад в любой из этих проектов, вам необходимо подписать (или подписать) Лицензионное соглашение участника (CLA). По-видимому, этого не было в августе 2015 года, когда был добавлен ваш ответ; @pborman добавил , что только на 16 февраля 2016 года .
Гвинет Ллевелин,