概述
36进制:使用 [0-9,a-z]
描述,逢36进一,即称之为36进制
var num2char = "0123456789abcdefghijklmnopqrstuvwxyz"
典型应用:汽车号码牌,比如: 京 B0540Z;顺序生成序号等
进制转换规则参考16进制与10进制转换,大体如下:
- 10进制转36进制,用10进制数除了36,取余数得最低1位,然后把商继续除36,得第二位,直到商为0。举例:
- 126110
- 1261 除 36 余数 35 (对应 Z),商 1
- 1 除 36 余数 1 (对应1),商 0 结束
- 得到 (Z1)36
- 36进制转10进制,36进制数按权展开,从右边第一个数开始,每一个乘以16的n次方,n从0开始,每次递增1,然后得出来的每个数相加即是十进制数。举例:
- (Z1)36 = 35 * 361+ 1 * 360 = 126110
- (ABC)36 = 10 * 362 + 11 * 361+ 12 * 360 = 1336810
golang 程序实现:
编码:
// 10进制数转换 n 表示进制, 16 or 36
func NumToBHex(num, n int) string {
num_str := ""
for num != 0 {
yu := num % n
num_str = string(num2char[yu]) + num_str
num = num / n
}
return strings.ToUpper(num_str)
}
解码
// 36进制数转换 n 表示进制, 16 or 36
func BHex2Num(str string, n int) int {
str = strings.ToLower(str)
v := 0.0
length := len(str)
for i := 0; i < length; i++ {
s := string(str[i])
index := strings.Index(num2char, s)
v += float64(index) * math.Pow(float64(n), float64(length-1-i)) // 倒序
}
return int(v)
}
测试
func TestBHex2Num(t *testing.T) {
assert.Equal(t, BHex2Num("1E6K", 36), 65036)
assert.Equal(t, BHex2Num("FE0C", 16), 65036)
assert.Equal(t, NumToBHex(65036, 36), "1E6K")
assert.Equal(t, NumToBHex(65036, 16), "FE0C")
}
应用: 生成序号
日期+[A-Z][0-Z]顺序生产,例如 20190520-A0 20190520-A1 ... 20190520-ZZ
// 按 yymmdd-[A-z][0-Z] 形式生成序号
func UssNoGenerate(last string) string {
prefix := time.Now().Format(DateTimeDate)
value := 0
if last != "" {
suffix := strings.Split(last, "-")[1]
value = BHex2Num(suffix, 36) + 1
} else {
value = BHex2Num("A0", 36)
}
return prefix + "-" + NumToBHex(value, 36)
}
应用测试:
func TestUssNoGenerate(t *testing.T) {
last := UssNoGenerate("")
for i := 0; i < 40; i++ {
fmt.Println(last)
last = UssNoGenerate(last)
}
}
// 输出
20190520-A0
20190520-A1
20190520-A2
20190520-A3
...
20190520-AZ
20190520-B0
20190520-B1
20190520-B2
20190520-B3
最开始的需求即是生成这些编号,使用字符串截取运算等方式,可以完成,但代码不忍直视,所以改进为些版本。至少原理上非常容易理解。
你看出bug来了吗,欢迎留言
网友评论