duandi8852752 2016-06-21 01:18
浏览 902
已采纳

在Go中以短字符串形式输出UUID

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 advantage of using a larger range of characters such as [A-Za-z0-9] to output a shorter string.

I know we can use base64 to encode the bytes, as follows, but I'm after something that creates a string that looks like a "word", i.e. no + and /:

id = base64.StdEncoding.EncodeToString(myUuid.Bytes())
  • 写回答

2条回答 默认 最新

  • dtpt75860 2016-06-21 12:31
    关注

    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
    ", 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
    ", dec, err)
    

    Output:

    123e4567e89b12d3a456426655440000, <nil>
    

    Try all the examples on the Go Playground.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?