如何在Golang中将数字转换为格式为1 = A1,2 = A2,…9 = B1,…64 = H8的字符串?

This code is giving A1..A9, B0..B9, C0..C9, ....

But I only want A1..A8, B1..B8, C1..C8, D1..D8, E1..E8, F1..F8, G1..G8, H1..H8 (1-64).

package main

import (
    "fmt"
)

func ToString(n int8) string {

    return string((n/10)+65) + string(((n%10)+49)-1)

}

func main() {
    var i int8
    for i = 1; i < 11; i++ {
        fmt.Println(ToString(i))
    }
}

Firstly, you're dividing by the wrong number. If you want the numbers only up to 8, you need to divide by 8.

Secondly, you don't need to subtract one in the second string, but you do need to decrease the n.

func ToString(n int8) string {
    n--
    return string((n/8)+65) + string((n%8)+49)
}

func main() {
    var i int8
    for i = 1; i <= 64; i++ {
        fmt.Println(ToString(i))
    }
}

https://play.golang.org/p/BdAce3C5JL

Let's see different approaches and performance improvements.

All the solutions and the benchmarking code can be found on the Go Playground. The code on the Playground is a test file, not an executable. You have to save it into a file named XX_test.go and run it with go test -bench ..

String concatenation

Ainar-G's answer is cool (+1):

func ToStringConcat(n byte) string {
    n--
    return string((n/8)+65) + string((n%8)+49)
}

Byte slice

But note that the previous solution concatenates 2 string values which is "costly" especially if we want to call this ToString() many times.

One might think that we could spare the string concatenation if we would try to convert a single integer number containing the 2 codes (the first shifted left by 8 bits), but that wouldn't work as converting integer numbers to string results in a string value with a single rune (a string containing the UTF-8 representation of the integer).

But we may use a []byte with 2 values (the letter code and the digit code), and then we only need to convert this single slice value to string:

func ToStringSlice(n byte) string {
    n--
    return string([]byte{(n / 8) + 65, (n % 8) + 49})
}

String constant and slicing

string values are slicable in Go which results in a new string (new string header). So we may use a string constant of all the values, and do a simple slicing to get the part we need:

const values = "  A1A2A3A4A5A6A7A8B1B2B3B4B5B6B7B8C1C2C3C4C5C6C7C8D1D2D3D4D5D6D7D8E1E2E3E4E5E6E7E8F1F2F3F4F5F6F7F8G1G2G3G4G5G6G7G8H1H2H3H4H5H6H7H8"

func ToStringConst(n byte) string {
    n *= 2
    return values[n : n+2]
}

Dictionary

Even though slicing a string results in a substring which shares the underlying array, it sill needs to create a new string header.

Since we don't have a lot of possible values, best (fastest) solution is to prepare all the possible values, and just do a simple lookup. Since input is a number, we don't even need a map, we can use a simple []string slice:

var dict = []string{"",
    "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8",
    "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8",
    "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8",
    "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8",
    "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8",
    "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8",
    "G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8",
    "H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8",
}

func ToStringDict(n byte) string {
    return dict[n]
}

Speed analysis (benchmark)

Let's benchmark speed of the above solutions:

func BenchmarkConcat(b *testing.B) {
    for i := 0; i < b.N; i++ { ToStringConcat(1) }
}

func BenchmarkSlice(b *testing.B) {
    for i := 0; i < b.N; i++ { ToStringSlice(1) }
}

func BenchmarkConst(b *testing.B) {
    for i := 0; i < b.N; i++ { ToStringConst(1) }
}

func BenchmarkDict(b *testing.B) {
    for i := 0; i < b.N; i++ { ToStringDict(1) }
}

Result:

BenchmarkConcat-4       20000000               106 ns/op
BenchmarkSlice-4        100000000               17.0 ns/op
BenchmarkConst-4        2000000000               1.34 ns/op
BenchmarkDict-4         2000000000               1.04 ns/op

Just by jumping from concatenation to slice conversion, it immediately becomes 6 times faster.

Utilizing string slicing, we again make it 12 times faster.

And pre-building all possible values and just doing a simple lookup, we further gain 22%.

Comparing the final to the initial: the dictionary lookup is a hundred times faster than the original concatenation.

Using fmt

For completeness, here's a solution using the fmt package:

func ToStringFmt(n byte) string {
    n--
    return fmt.Sprintf("%c%c", (n/8)+65, (n%8)+49)
}

But this one is slower by almost 2.5 times even than our slowest String concatenation solution, as this has to wrap arguments into interface{} values, create and put them into a slice (for the vararg), has to parse and analyze a format string, use reflection to process arguments, build the string representation in a buffer which finally is used to produce the returned string value. A lot of things happen which are for the "general" case and are not required for our "special" case.