Output UUID in Go as a short string

后端 未结 2 987
囚心锁ツ
囚心锁ツ 2021-01-31 23:19

Is there a built in way, or reasonably standard package that allows you to convert a standard UUID into a short string that would enable shorter URL\'s?

I.e. taking adva

相关标签:
2条回答
  • 2021-01-31 23:44

    As suggested here, If you want just a fairly random string to use as slug, better to not bother with UUID at all.

    You can simply use go's native math/rand library to make random strings of desired length:

    import (
    "math/rand"
    "encoding/hex"
    )
    
    
    b := make([]byte, 4) //equals 8 charachters
    rand.Read(b) 
    s := hex.EncodeToString(b)
    
    0 讨论(0)
  • 2021-01-31 23:46

    A universally unique identifier (UUID) is a 128-bit value, which is 16 bytes. For human-readable display, many systems use a canonical format using hexadecimal text with inserted hyphen characters, for example:

    123e4567-e89b-12d3-a456-426655440000
    

    This has length 16*2 + 4 = 36. You may choose to omit the hypens which gives you:

    fmt.Printf("%x\n", uuid)
    fmt.Println(hex.EncodeToString(uuid))
    
    // Output: 32 chars
    123e4567e89b12d3a456426655440000
    123e4567e89b12d3a456426655440000
    

    You may choose to use base32 encoding (which encodes 5 bits with 1 symbol in contrast to hex encoding which encodes 4 bits with 1 symbol):

    fmt.Println(base32.StdEncoding.EncodeToString(uuid))
    
    // Output: 26 chars
    CI7EKZ7ITMJNHJCWIJTFKRAAAA======
    

    Trim the trailing = signs when transmitting, so this will always be 26 chars. Note that you have to append "======" prior to decode the string using base32.StdEncoding.DecodeString().

    If this is still too long for you, you may use base64 encoding (which encodes 6 bits with 1 symbol):

    fmt.Println(base64.RawURLEncoding.EncodeToString(uuid))
    
    // Output: 22 chars
    Ej5FZ-ibEtOkVkJmVUQAAA
    

    Note that base64.RawURLEncoding produces a base64 string (without padding) which is safe for URL inclusion, because the 2 extra chars in the symbol table (beyond [0-9a-zA-Z]) are - and _, both which are safe to be included in URLs.

    Unfortunately for you, the base64 string may contain 2 extra chars beyond [0-9a-zA-Z]. So read on.

    Interpreted, escaped string

    If you are alien to these 2 extra characters, you may choose to turn your base64 string into an interpreted, escaped string similar to the interpreted string literals in Go. For example if you want to insert a backslash in an interpreted string literal, you have to double it because backslash is a special character indicating a sequence, e.g.:

    fmt.Println("One backspace: \\") // Output: "One backspace: \"
    

    We may choose to do something similar to this. We have to designate a special character: be it 9.

    Reasoning: base64.RawURLEncoding uses the charset: A..Za..z0..9-_, so 9 represents the highest code with alphanumeric character (61 decimal = 111101b). See advantage below.
    So whenever the base64 string contains a 9, replace it with 99. And whenever the base64 string contains the extra characters, use a sequence instead of them:

    9  =>  99
    -  =>  90
    _  =>  91
    

    This is a simple replacement table which can be captured by a value of strings.Replacer:

    var escaper = strings.NewReplacer("9", "99", "-", "90", "_", "91")
    

    And using it:

    fmt.Println(escaper.Replace(base64.RawURLEncoding.EncodeToString(uuid)))
    
    // Output:
    Ej5FZ90ibEtOkVkJmVUQAAA
    

    This will slightly increase the length as sometimes a sequence of 2 chars will be used instead of 1 char, but the gain will be that only [0-9a-zA-Z] chars will be used, as you wanted. The average length will be less than 1 additional character: 23 chars. Fair trade.

    Logic: For simplicity let's assume all possible uuids have equal probability (uuid is not completely random, so this is not the case, but let's set this aside as this is just an estimation). Last base64 symbol will never be a replaceable char (that's why we chose the special char to be 9 instead of like A), 21 chars may turn into a replaceable sequence. The chance for one being replaceable: 3 / 64 = 0.047, so on average this means 21*3/64 = 0.98 sequences which turn 1 char into a 2-char sequence, so this is equal to the number of extra characters.

    To decode, use an inverse decoding table captured by the following strings.Replacer:

    var unescaper = strings.NewReplacer("99", "9", "90", "-", "91", "_")
    

    Example code to decode an escaped base64 string:

    fmt.Println("Verify decoding:")
    s := escaper.Replace(base64.RawURLEncoding.EncodeToString(uuid))
    dec, err := base64.RawURLEncoding.DecodeString(unescaper.Replace(s))
    fmt.Printf("%x, %v\n", dec, err)
    

    Output:

    123e4567e89b12d3a456426655440000, <nil>
    

    Try all the examples on the Go Playground.

    0 讨论(0)
提交回复
热议问题