整形转字符串经常会用到,本文讨论一下 Golang 提供的这几种方法。基于 go1.10.1

Golang 中整数转字符串的方法(golang数字转字符串)  整数 字符串 脚本之家 第1张

fmt.Sprintf

fmt 包应该是最常见的了,从刚开始学习 Golang 就接触到了,写 ‘hello, world' 就得用它。它还支持格式化变量转为字符串。

func Sprintf(format string, a ...interface{}) string
Sprintf formats according to a format specifier and returns the resulting string.
fmt.Sprintf("%d", a)

%d 代表十进制整数。

strconv.Itoa

func Itoa(i int) string
Itoa is shorthand for FormatInt(int64(i), 10).
strconv.Itoa(a)

strconv.FormatInt

func FormatInt(i int64, base int) string
FormatInt returns the string representation of i in the given base, for 2 <= base <= 36. The result uses the lower-case letters ‘a' to ‘z' for digit values >= 10.

参数 i 是要被转换的整数, base 是进制,例如2进制,支持2到36进制。

strconv.Format(int64(a), 10)

Format 的实现

[0, 99)的两位整数

对于小的(小于等于100)十进制正整数有加速优化算法:

if fastSmalls && 0 <= i && i < nSmalls && base == 10 {
 return small(int(i))
}

加速的原理是提前算好100以内非负整数转换后的字符串。

const smallsString = "00010203040506070809" +
 "10111213141516171819" +
 "20212223242526272829" +
 "30313233343536373839" +
 "40414243444546474849" +
 "50515253545556575859" +
 "60616263646566676869" +
 "70717273747576777879" +
 "80818283848586878889" +
 "90919293949596979899"

可以看出来,转换后的结果是从1到99都有,而且每个结果只占两位。当然个人数的情况还得特殊处理,个位数结果只有一位。

func small(i int) string {
 off := 0
 if i < 10 {
  off = 1
 }
 return smallsString[i*2+off : i*2+2]
}

如果被转换的数字是个位数,那么偏移量变成了1,默认情况是0。

只支持2到36进制的转换。36进制是10个数字加26个小写字母,超过这个范围无法计算。

var a [64 + 1]byte

整形最大64位,加一位是因为有个符号。转换计算时,要分10进制和非10进制的情况。

10进制转换

10进制里,两位两位转换,为什么这么干?两位数字时100以内非负整数转换可以用上面的特殊情况加速。很有意思。

us := uint(u)
for us >= 100 {
 is := us % 100 * 2
 us /= 100
 i -= 2
 a[i+1] = smallsString[is+1]
 a[i+0] = smallsString[is+0]
}

2、4、8、16、32进制的转换。

const digits = "0123456789abcdefghijklmnopqrstuvwxyz"

var shifts = [len(digits) + 1]uint{
  1 << 1: 1,
  1 << 2: 2,
  1 << 3: 3,
  1 << 4: 4,
  1 << 5: 5,
}

if s := shifts[base]; s > 0 {
 // base is power of 2: use shifts and masks instead of / and %
 b := uint64(base)
 m := uint(base) - 1 // == 1<<s - 1
 for u >= b {
 i--
 a[i] = digits[uint(u)&m]
 u >>= s
 }
 // u < base
 i--
 a[i] = digits[uint(u)]
}

通过循环求余实现。进制的转换也是这种方式。

func Itoa(i int) string
Itoa is shorthand for FormatInt(int64(i), 10).
strconv.Itoa(a)0

上面的代码实现了进制的转换。而 digits[uint(u)&m] 实现了转换后的结果再转成字符。

常规情况

func Itoa(i int) string
Itoa is shorthand for FormatInt(int64(i), 10).
strconv.Itoa(a)1

依然是循环求余来实现。这段代码更像是给人看的。和上面2的倍数的进制转换的区别在于,上面的代码把除法 / 换成了右移( >> ) s 位,把求余 % 换成了逻辑与 & 操作。

Sprintf 的实现

func Itoa(i int) string
Itoa is shorthand for FormatInt(int64(i), 10).
strconv.Itoa(a)2

判断类型,如果是整数 int 类型,不需要反射,直接计算。支持的都是基础类型,其它类型只能通过反射实现。

Sprintf 支持的进制只有10 %d 、16 x 、8 o 、2 b 这四种,其它的会包 fmt: unknown base; can't happen 异常。

func Itoa(i int) string
Itoa is shorthand for FormatInt(int64(i), 10).
strconv.Itoa(a)3

2、8、16进制和之前 FormatInt 差不多,而10进制的性能差一些,每次只能处理一位数字,而不像 FormatInt 一次处理两位。

性能对比

func Itoa(i int) string
Itoa is shorthand for FormatInt(int64(i), 10).
strconv.Itoa(a)4

压测有三组对比,小于100的情况,大数字的情况,还有二进制的情况。

func Itoa(i int) string
Itoa is shorthand for FormatInt(int64(i), 10).
strconv.Itoa(a)5
  1. Sprintf 在所有情况中都是最差的,还是别用这个包了。
  2. 小于100的情况会有加速,不光是性能上的加速,因为结果是提前算好的,也不需要申请内存。
  3. FormatInt 10进制性能最好,其它的情况差一个数量级。
  4. Itoa 虽然只是封装了 FormatInt ,对于性能还是有一些影响的。

本文涉及的代码可以从 这里 下载。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持ASPKU源码库。


注:相关教程知识阅读请移步到Golang频道。