如何在 Golang 中将数字转换为 1=A1, 2=A2, ... 9=B1, ... 64=H8 形式的字符串?
How to convert a number to string, of the form 1=A1, 2=A2, ... 9=B1, ... 64=H8 in Golang?
此代码给出 A1..A9, B0..B9, C0..C9, ...
。
但我只想要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))
}
}
首先,您除以错误的数字。如果您只希望数字最多为 8,则需要除以 8。
其次,你不需要在第二个字符串中减一,但你确实需要减少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))
}
}
让我们看看不同的方法和性能改进。
所有解决方案和基准测试代码都可以在 Go Playground 上找到。 Playground 上的代码是测试文件,而不是可执行文件。您必须将其保存到名为 XX_test.go
和 运行 的文件中,并使用 go test -bench .
.
字符串连接
Ainar-G的回答很酷(+1):
func ToStringConcat(n byte) string {
n--
return string((n/8)+65) + string((n%8)+49)
}
字节切片
但请注意,之前的解决方案连接了 2 个 string
值,即 "costly",特别是如果我们想多次调用此 ToString()
。
如果我们尝试转换包含 2 个代码(第一个左移 8 位)的单个整数,我们可能会认为我们可以省去 string
连接,但是将整数转换为 string
不会产生 string
值和单个 rune
(string
包含整数的 UTF-8 表示)。
但是我们可以用一个[]byte
有2个值(字母代码和数字代码),然后我们只需要convert这个单片值到string
:
func ToStringSlice(n byte) string {
n--
return string([]byte{(n / 8) + 65, (n % 8) + 49})
}
字符串常量和切片
string
值在 Go 中是 slicable,这会导致新的 string
(新的 string
header)。所以我们可以使用所有值的 string
常量,并进行简单的切片以获得我们需要的部分:
const values = " A1A2A3A4A5A6A7A8B1B2B3B4B5B6B7B8C1C2C3C4C5C6C7C8D1D2D3D4D5D6D7D8E1E2E3E4E5E6E7E8F1F2F3F4F5F6F7F8G1G2G3G4G5G6G7G8H1H2H3H4H5H6H7H8"
func ToStringConst(n byte) string {
n *= 2
return values[n : n+2]
}
词典
即使对 string
进行切片会得到一个共享底层数组的子字符串,它仍然需要创建一个新的 string
header.
由于我们没有很多可能的值,最好(最快)的解决方案是准备所有可能的值,然后进行简单的查找。由于输入是一个数字,我们甚至不需要 map
,我们可以使用一个简单的 []string
切片:
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]
}
速度分析(基准)
让我们对上述解决方案的速度进行基准测试:
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) }
}
结果:
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
只需从连接跳到切片转换,它立即变得 6 倍。
利用字符串切片,我们再次使它 快 12 倍。
和 pre-building 所有可能的值,只需进行简单的查找,我们进一步 获得 22%。
比较最终与初始:字典查找比原始连接快一百倍。
使用fmt
为了完整起见,这里有一个使用 fmt
包的解决方案:
func ToStringFmt(n byte) string {
n--
return fmt.Sprintf("%c%c", (n/8)+65, (n%8)+49)
}
但是这个甚至比我们最慢的字符串连接解决方案慢了近 2.5 倍,因为它必须将参数包装到 interface{}
值中,创建并将它们放入一个切片(对于 vararg),具有解析和分析格式 string
,使用反射处理参数,在缓冲区中构建 string
表示,最终用于生成返回的 string
值。 "general" 案例发生了很多事情,而我们的 "special" 案例不需要。
此代码给出 A1..A9, B0..B9, C0..C9, ...
。
但我只想要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))
}
}
首先,您除以错误的数字。如果您只希望数字最多为 8,则需要除以 8。
其次,你不需要在第二个字符串中减一,但你确实需要减少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))
}
}
让我们看看不同的方法和性能改进。
所有解决方案和基准测试代码都可以在 Go Playground 上找到。 Playground 上的代码是测试文件,而不是可执行文件。您必须将其保存到名为 XX_test.go
和 运行 的文件中,并使用 go test -bench .
.
字符串连接
Ainar-G的回答很酷(+1):
func ToStringConcat(n byte) string {
n--
return string((n/8)+65) + string((n%8)+49)
}
字节切片
但请注意,之前的解决方案连接了 2 个 string
值,即 "costly",特别是如果我们想多次调用此 ToString()
。
如果我们尝试转换包含 2 个代码(第一个左移 8 位)的单个整数,我们可能会认为我们可以省去 string
连接,但是将整数转换为 string
不会产生 string
值和单个 rune
(string
包含整数的 UTF-8 表示)。
但是我们可以用一个[]byte
有2个值(字母代码和数字代码),然后我们只需要convert这个单片值到string
:
func ToStringSlice(n byte) string {
n--
return string([]byte{(n / 8) + 65, (n % 8) + 49})
}
字符串常量和切片
string
值在 Go 中是 slicable,这会导致新的 string
(新的 string
header)。所以我们可以使用所有值的 string
常量,并进行简单的切片以获得我们需要的部分:
const values = " A1A2A3A4A5A6A7A8B1B2B3B4B5B6B7B8C1C2C3C4C5C6C7C8D1D2D3D4D5D6D7D8E1E2E3E4E5E6E7E8F1F2F3F4F5F6F7F8G1G2G3G4G5G6G7G8H1H2H3H4H5H6H7H8"
func ToStringConst(n byte) string {
n *= 2
return values[n : n+2]
}
词典
即使对 string
进行切片会得到一个共享底层数组的子字符串,它仍然需要创建一个新的 string
header.
由于我们没有很多可能的值,最好(最快)的解决方案是准备所有可能的值,然后进行简单的查找。由于输入是一个数字,我们甚至不需要 map
,我们可以使用一个简单的 []string
切片:
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]
}
速度分析(基准)
让我们对上述解决方案的速度进行基准测试:
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) }
}
结果:
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
只需从连接跳到切片转换,它立即变得 6 倍。
利用字符串切片,我们再次使它 快 12 倍。
和 pre-building 所有可能的值,只需进行简单的查找,我们进一步 获得 22%。
比较最终与初始:字典查找比原始连接快一百倍。
使用fmt
为了完整起见,这里有一个使用 fmt
包的解决方案:
func ToStringFmt(n byte) string {
n--
return fmt.Sprintf("%c%c", (n/8)+65, (n%8)+49)
}
但是这个甚至比我们最慢的字符串连接解决方案慢了近 2.5 倍,因为它必须将参数包装到 interface{}
值中,创建并将它们放入一个切片(对于 vararg),具有解析和分析格式 string
,使用反射处理参数,在缓冲区中构建 string
表示,最终用于生成返回的 string
值。 "general" 案例发生了很多事情,而我们的 "special" 案例不需要。