Чи існує спосіб створення 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?


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

23
Зокрема, такий підхід повільний; на MacBook Air 2012 року ця стратегія може виробляти лише 170 уд / с.
Джей Тейлор

12
І за допомогою бібліотеки nu7hatch / gouuid я зміг генерувати 172 488 уд / сек.
Джей Тейлор

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 версії 1 на основі вашої MAC адреси та часу).


2
@Flimzy для людей, які не знають, що вони роблять, це, швидше за все, правда. Вводити зайві залежності - це завжди погано.
Ерік Ейгнер

31
@ErikAigner Поки це 50 рядків, я не повинен думати, писати і тестувати, я візьму їх подяку .. У мене є інші речі, щоб зробити потім винаходити колесо.
RickyA

3
Схоже, ця бібліотека насправді не сумісна з RFC4122: github.com/nu7hatch/gouuid/isissue/28 (наразі відкрите видання станом на 1.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 - це інтерфейс, а базовий тип - це масив.

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


4
Мені подобається цей пакет. Я офіційно прийняв його для всіх моїх заявок. Я виявив, що пакет nu7hatch не відповідає RFC4122.
Річард Енг

+1 Погоджено, оновлення та розширення для друку / форматування вже включені.
eduncan911

4
Відмова відсутня? : p
чакрит

3
Що таке бібліотека «внизу»? Вам слід уникати використання вище та нижче для SO, оскільки це може змінитися досить швидко.
Стефан Доллберг

Є ще одна рівнозначна, satori / go.uuid . Ще не спробував, але я буду використовувати його як заміну nu7hatch мертвого проекту ...
shadyyx

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

Дякую! Досить просто
Карл Покус

1. Це не відповідає жодному стандарту. 2. Якщо у вас %xє проблеми зі значеннями байтів, що не перевищують 128, потрібно застосувати прокладку, тобто %04xдля пари байтів
Ja͢ck,

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())
Джим

@Jim: ти маєш рацію! Я відповідно оновив свою відповідь.
shutefan

Зауважте, що New () може бути "фатальним" (що в деяких випадках нормально). У випадках, коли ви не хочете, щоб ваша програма стала фатальною, просто використовуйте uuid.NewRandom () - який повертає UUID та помилку.
Томер

@Tomer: правда! Хоча мені цікаво, за яких обставин це насправді відбуватиметься. Це відповідна частина коду: github.com/google/uuid/blob/… За замовчуванням читачем є rand.Reader. Я не впевнений, що коли-небудь поверне помилку чи це може статися лише із користувацьким Reader ...
shutefan

1
Привіт @shutefan - я згоден, що це може бути рідко. rand.Reader викликає функції ядра ( golang.org/src/crypto/rand/rand.go ). Вони можуть провалюватися в певних сценаріях.
Томер


12

З поста Русса Кокса :

Офіційної бібліотеки немає. Ігноруючи перевірку помилок, схоже, це буде добре:

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, хоча: версія 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:]). У поєднанні з кодом ОП, і це чудово працює.
chowey

2
Використання пакету crypto / rand: play.golang.org/p/7JJDx4GL77 . Код zzzz робить те, що робить crypt / rand, за винятком того, що він також охоплює платформи, які не підтримують / dev / urandom (Windows).
Дрю

Слід зазначити, що це специфічно для платформи
Dan Esparza

2
@Matt: проблема полягає в тому, що інші формати UUID отримують свою унікальність шляхом делегування до якогось іншого органу (наприклад, що ваша Mther-адреса 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.

Він також використовує пакет криптовалют / 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

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
Зрозуміло, тому що безпосередньо залежно від хост-платформи мовою програмування, яка використовується для багатоплатформних додатків, гірше зовнішньої залежності.
побачення

1
@ Byebye, мені цікаво, чому ти вважаєш себе повноваженням вирішувати, що "гірше" (а що ні) переглядати всі відповіді на це запитання та скасовувати всі, які "залежать від системи"? Ці відповіді були дані: а) розширити горизонт всіх можливих варіантів; б) колективно представити повну картину. Тому, будь ласка, перестаньте по-дитячому "грати в ТАК" і подумайте перед вами.
kostix

Коротка відповідь. Написання ремонтованого коду. Вашу відповідь не можна перенести на іншу платформу. Тож якщо ОП вирішить перенести свою програму на іншу платформу, програма застосується. Я маю неабияку частку людей, які написали код, залежний від платформи, там, де він зовсім непотрібний, і це створює більше проблем, ніж це варто. Ви не пишете код тільки для себе. Ви пишете код для людей, які підтримуватимуть його після того, як ви підете. Ось чому ця відповідь не підходить. Немає приводу вдаватися до ad hominems і називати мене по-дитячому.
побачення

1
@Byebye, я зреагував, тому, будь ласка, вибачте мене за напад. Я все ще не переконаний у ваших причинах, але нібито справа в тому, що "погодимося не погодитися".
kostix

1

Ця бібліотека є нашим стандартом для генерації та розбору uuid:

https://github.com/pborman/uuid


Зауважте, що власна бібліотека Google ( github.com/google/uuid ) частково базується на github.com/pborman/uuid , яка, в свою чергу, включила деякі зміни, внесені Google. Однак, нібито, якщо ви хочете внести свій внесок у будь-який з цих проектів, вам потрібно підписати (або підписати) ліцензійну угоду для учасників (CLA). Мабуть, це не було в серпні 2015 року, коли ваша відповідь була додана; @pborman додав , що тільки на 16 лютого 2016 року .
Гвінет Левелін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.