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())
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:
This has length
16*2 + 4 = 36
. You may choose to omit the hypens which gives you: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):
Trim the trailing
=
signs when transmitting, so this will always be 26 chars. Note that you have to append"======"
prior to decode the string usingbase32.StdEncoding.DecodeString()
.If this is still too long for you, you may use base64 encoding (which encodes 6 bits with 1 symbol):
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.:
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-_
, so9
represents the highest code with alphanumeric character (61 decimal = 111101b). See advantage below.So whenever the base64 string contains a
9
, replace it with99
. And whenever the base64 string contains the extra characters, use a sequence instead of them:This is a simple replacement table which can be captured by a value of
strings.Replacer
:And using it:
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 likeA
), 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
:Example code to decode an escaped base64 string:
Output:
Try all the examples on the Go Playground.
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: