Excel為了方便區分行和列,對行號采用十進制整數遞增表示,列號采用一種特殊的“二十六進制”表示。十進制在人類的日常生活中廣泛使用,是最容易被理解的整數表示法。對計算機來說,由于半導體晶體管的物理特性,使用二進制作為內部運行的整數表示法(浮點數的表示法和整數不同,浮點數需要特殊的寄存器支持)。在編寫代碼的時候,通常使用十六進制表示法。一個十六進制數占4bit,很容易直觀看出十六進制字面量占用的位數,這在編寫跨平臺程序時尤為重要。如果直接使用二進制,一方面可讀性不好,另一方面書寫負擔也很嚴重(試想一下數清楚一長串的0和1是什么概念)。
十六進制使用 '0' ~ '9'和'A' ~ 'Z' (或'a' ~ 'z')表示,這16個十六進制數分別對應十進制整數序列0~15。Excel使用'A' ~ 'Z'(或'a' ~ 'z')作為二十六進制數字表示列號。類似十六進制,從0開始計數,低位表示溢出時向高位進位。例如,A表示0, B表示1,Z表示25,AA表示26, AZ表示51, BA表示52。注意16的十六進制字符串表示是10,26的"二十六進制"字符串表示時AA,可以從這點著手思考它們與十進制的轉換方法的細微差別。
下面兩個函數(Go語言編寫)演示了十六進制和“二十六進制”字符串與十進制整數進行互相轉換的算法:
func ConvertToDecimal(str string, dict map[byte]int, start byte, carry int) int {
str = strings.ToUpper(str)
ret := 0
base := len(dict)
for i, _ := range str {
ch := str[len(str)-i-1]
v1 := dict[ch] - dict[start] + carry
v2 := int(math.Pow(float64(base), float64(i)))
ret += v1 * v2
}
return ret
}
func ConvertFromDecimal(n int, dict map[int]byte, carry int) string {
ret := ""
base := len(dict)
n -= carry
for {
q := n / base // quotient
r := n % base // remainder
ret = string(dict[r]) + ret
if q == 0 {
break
}
n = q - carry
}
return ret
}
因為十六進制數的ASCII碼并不是連續的,為了函數通用,增加了dict參數作為不同進制數和十進制整數的映射表,它們分別是:
var hexDict = map[byte]int{
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
'5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15,
}
var alphaDict = map[byte]int{
'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4,
'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9,
'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14,
'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19,
'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24,
'Z': 25,
}
反轉映射表函數:
func reverse(dict map[byte]int) map[int]byte {
ret := map[int]byte{}
for k, v := range dict {
ret[v] = k
}
return ret
}
測試用例:
func Example_ConvertToDecimal() {
// 十六進制
list := []string{"0", "1", "A", "F", "10", "FF", "F10"}
results := []int{}
for _, str := range list {
results = append(results, ConvertToDecimal(str, hexDict, '0', 0))
}
// 二十六進制
list = []string{"A", "Z", "AA", "AZ", "GT", "AAA", "JQP"}
for _, str := range list {
results = append(results, ConvertToDecimal(str, alphaDict, 'A', 1)-1)
}
fmt.Println(results)
// Output:
// [0 1 10 15 16 255 3856 0 25 26 51 201 702 7217]
}
func Example_ConvertFromDecimal() {
// 十六進制
list := []int{0, 1, 10, 15, 16, 255, 3856}
results := []string{}
for _, n := range list {
results = append(results, ConvertFromDecimal(n, reverse(hexDict), 0))
}
// 二十六進制
list = []int{0, 25, 26, 51, 201, 702, 7217}
for _, n := range list {
results = append(results, ConvertFromDecimal(n+1, reverse(alphaDict), 1))
}
fmt.Println(results)
// Output:
// [0 1 A F 10 FF F10 A Z AA AZ GT AAA JQP]
}
如果你讀到這,那么請思考一下:
- carry的區別,還記得前面提到10和AA嗎?
- 為什么不用三十二,六十四進制?