我假設我需要做的就是將 2^64 編碼為 base64 以獲得 11 個字符的 Youtube 識別符號。我創建了一個 Go 程式https://play.golang.org/p/2nuA3JxVMd0
package main
import (
"crypto/rand"
"encoding/base64"
"encoding/binary"
"fmt"
"math"
"math/big"
"strings"
)
func main() {
// For example Youtube uses 11 characters of base64.
// How many base64 characters would it require to express a 2^64 number? 2^6^x = 2^64 .. so x = 64/6 = 10.666666666 … i.e. eleven rounded up.
// Generate a 64 bit number
val, _ := randint64()
fmt.Println(val)
// Encode the 64 bit number
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(val))
encoded := base64.StdEncoding.EncodeToString([]byte(b))
fmt.Println(encoded, len(encoded))
// https://youtu.be/gocwRvLhDf8?t=75
ytid := strings.ReplaceAll(encoded, " ", "-")
ytid = strings.ReplaceAll(ytid, "/", "_")
fmt.Println("Youtube ID from 64 bit number:", ytid)
}
func randint64() (int64, error) {
val, err := rand.Int(rand.Reader, big.NewInt(int64(math.MaxInt64)))
if err != nil {
return 0, err
}
return val.Int64(), nil
}
但它有兩個問題:
- 識別符號是 12 個字符而不是預期的 11 個字符
- 編碼后的 base64 后綴是“=”,這意味著它沒有足夠的編碼?
那么我哪里出錯了?
uj5u.com熱心網友回復:
tl;博士
8 位元組int64(無論是什么值)將始終編碼為11base64 位元組,后跟單個填充位元組=,因此您可以可靠地執行此操作以獲得您的11字符YouTubeID:
var replacer = strings.NewReplacer(
" ", "-",
"/", "_",
)
ytid := replacer.Replace(encoded[:11])
或(H / T @Crowman&@Peter)沒有填充一個可編碼&無需更換 和/與base64.RawURLEncoding:
//encoded := base64.StdEncoding.EncodeToString(b) // may include or /
ytid := base64.RawURLEncoding.EncodeToString(b) // produces URL-friendly - and _
https://play.golang.org/p/AjlvtfR7RWD
一個byte(即 8 位)Base64 輸出傳送6 位輸入。因此,確定給定輸入的輸出位元組數的公式是:
out = in * 8 / 6
或者
out = in * 4 / 3
3在某些情況下,這會導致部分使用輸出位元組。如果輸入位元組長度為:
- 可被 3 整除 - 最后一個位元組落在位元組邊界上
- 不能被 3 整除 - 最后一個位元組不在位元組邊界上,需要填充
在輸入 8 位元組的情況下:
out = 8 * 4 / 3 = 10 2/3
將10充分利用輸出 base64 位元組 - 和一個部分位元組(用于2/3) - 所以11base64 位元組加上填充來指示浪費了多少位。
填充通過=字符=表示,數量表示“浪費”位的數量:
waste padding
===== =======
0
1/3 =
2/3 ==
由于輸出產生10 2/3使用過的位元組 - 然后1/3位元組被“浪費”,因此填充是單個=
因此,base64 編碼8輸入位元組將始終生成11base64 位元組,后跟單個=填充字符以生成12總共位元組。
uj5u.com熱心網友回復:
= 在 base64 中是填充,但在 64 位數字中,這個填充是額外的,不需要 12 個字符,但為什么呢?
見Encoding.Encode函式源:
func (enc *Encoding) Encode(dst, src []byte) {
if len(src) == 0 {
return
}
// enc is a pointer receiver, so the use of enc.encode within the hot
// loop below means a nil check at every operation. Lift that nil check
// outside of the loop to speed up the encoder.
_ = enc.encode
di, si := 0, 0
n := (len(src) / 3) * 3
//https://golang.org/src/encoding/base64/base64.go
在這(len(src) / 3) * 3部分中,使用3代替6
所以這個函式的輸出總是長度為偶數的字串,如果你的輸入總是64位,你可以=在編碼后洗掉并再次添加以進行解碼。
for i := 8; i <= 18; i {
b := make([]byte, i)
binary.LittleEndian.PutUint64(b, uint64(0))
encoded := base64.StdEncoding.EncodeToString(b)
fmt.Println(encoded)
}
AAAAAAAAAAA=
AAAAAAAAAAAA
AAAAAAAAAAAAAA==
AAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA==
AAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAA==
AAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAA
6(或3)是什么意思?
base64 使用 64 個字符,每個字符映射一個值(從 000000 到 111111)
例子:
64 位值 (uint64):
11154013587666973726
二進制表示:
1001101011001011000001000100001011110000110001010011010000011110
拆分每六位數字:
001001,101011,001011,000001,000100,001011,110000,110001,010011,010000,011110
J, r, L, B, E, L, w, x, T, Q, e
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/334892.html
