作为初学者,我们可能常常首先接触到的是 fmt
包,但是对于基础类型的转换而言, strconv
包更加高效且允许编译器类型检查。
strconv
针对四种基础类型提供了相应的函数:布尔值,整数,浮点数和字符串。
整数操作
-
如何从字符串中解析出有符号整数和无符号整数?
我们可以使用
ParseInt
和ParseUint
函数,它们的函数定义如下func ParseInt(s string, base int, bitSize int) (int64, error) func ParseUint(s string, base int, bitSize int) (uint64, error)
这两个函数读取s然后基于给定的
base
来转换成指定进制的数据,base可以是2-36的任意整数。我们也可以用base=0
表示基于s来决定进制:如果是0x
前缀则是十六进制,如果是0
前缀则是八进制,否则就是十进制。bitSize
指定的是解析后的整数的大小,如8表示int8
,16表示int16
等。如果为0表示指定int
-
什么时候会返回错误?返回什么错误?
最明显的错误莫过于包含了非法字符,如’a’,或者指定八进制下的’9’等,此时会返回
Err
字段为ErrSyntax
的NumError
另一个错误是解析的整数大于指定的
bitSize
。比方说解析"300"
对于bitSize
为8的时候就是非法的,因为int8
最大值为127,此时会返回Err
字段为ErrRange
的NumError
-
字符串解析为整数的简易函数?
我们可以使用
Atoi
函数,函数定义如下func Atoi(s string) (int, error)
在内部,该函数会调用
ParseInt(s,10,0)
,注意Atoi返回的是int而ParseInt返回的是int64由于一般情况下都使用int(除非想要节省内存或者需要在32位系统上使用64位大小),所以通常我们可以用
Atoi
替代ParseInt
-
-
如何将有/无符号整数转化为字符串
我们可以使用FormatInt
和FormatUint
函数,函数定义如下func FormatInt(i int64, base int) string func FormatUint(i uint64, base int) string
这两个函数会首先将i转换为指定进制然后返回字符串表达,base支持2-36进制
这两个函数在内部对于十进制以及2为底数的进制做了大量的优化。
-
如何使用更少的内存分配来转换
Format
系列的函数最大的问题在于每次都会返回一个新的字符串,而内存分配是性能的杀手。为此我们可以改为使用Append
系列函数,如AppendInt
和AppendUint
函数func AppendInt(dst []byte, i int64, base int) []byte func AppendUint(dst []byte, i uint64, base int) []byte
Append
函数会将结果写入到指定的dst中,如果dst容量不够,那么就会触发扩容,此时就需要通过返回值来重新获取地址了(类似于append()
扩容)在使用
Append
函数之前,我们需要计算好需要的buffer大小。以int16为例,int16最大值为32767,5位,考虑到负数,因此需要长度为6的字节数组,以下图为例a := []int16{-80, 100, 362, 32000} var buf [6]byte for _, v := range a { //这里[:0]是将其转换为切片 b := strconv.AppendInt(buf[:0], int64(v), 10) // Do something with b }
-
浮点数操作
-
如何从字符串中解析出浮点数?
我们可以使用
ParseFloat
函数,函数定义如下func ParseFloat(s string, bitSize int) (float64, error)
该函数会解析s然后返回一个符合bitSize(可以是32或者64)要求的浮点数。
如果我们试图解析一个过大或者过小的浮点数,那么会返回Err字段为
ErrRange
的NumError
,同时结果为+Infinity
或者-Infinity
浮点数转换包含了大量的优化以及位操作,因此如果感兴趣可以查看
atof.go
文件 -
如何将浮点数转换为字符串
我们可以使用FormatFloat函数,函数定义如下
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
其中,fmt会决定返回的字符串的格式:
-
'f'
不使用任何E来转换,比方说浮点数123.45会转换为"123.45"
-
'e'
或者'E'
强制使用E来转换,比方说对于浮点数
123.45
,会转换为"1.2345E+02"
或者"1.2345e+02"
,取决于传入的大小写 -
'g'
或者'G'
如果是小浮点数,直接输出。如果是大浮点数,使用E。判断大小的标准取决于
prec
-
'b'
这个非常容易迷惑人,其他的格式都是用十进制10n,而该格式使用的是二进制2n,比方说浮点数
64.0
配合bitSize
为32的时候返回8388608p-17
因为8388608 × (2-17)=64。一般很少使用该格式
prec则决定的是精度,比方说对于浮点数
3.14159
而言,prec为2下返回的字符串是3.14
。如果设置prec=-1
那么会基于bitSize
来决定精度bitSize会决定应该将f视为float32还是float64,如上面所言,这会决定精度
-
布尔值操作
-
如何从字符串中解析出布尔值?
我们可以使用
ParseBool
函数,该函数定义如下func ParseBool(str string) (bool, error)
该函数会将一系列的关于true和false字符串转换为true和false。true包括
1
,t
,T
,true
,True
,TRUE
;false包括0
,f
,F
,false
,False
,FALSE
。除此之外的所有输入都会返回错误通常情况下我们可能不需要那么宽泛的true或者false概念,比方说在我们场景下只有"true"或者"false"
-
如何将布尔值转换为字符串?
我们可以使用
FormatBool
来将布尔值转换为字符串。函数定义如下:func FormatBool(b bool) string
该函数会根据传入值返回"true"或者"false"
除此之外我们也可以使用
AppendBool
函数
字符串操作
可能有些人会奇怪为什么字符串还需要针对字符串操作的函数,这通常用于显示字符串中的控制字符(如\t
)以及不可打印字符
-
Quote字符串(不知道怎么翻译)
我们可以使用
Quote
来实现,函数定义如下func Quote(s string) string
该函数会将字符串中的tab符、换行符以及不可打印字符用转义序列转换成
\t
,\n
和\uXXXX
。该函数可以用于显示错误消息因为数据可能包含奇怪的字符如果需要限定显示字符串范围为ASCII字符(即将中文字符等也转义),那么可以使用
QuoteToASCII
函数,函数定义如下func QuoteToASCII(s string) string
除此之外还有一个
QuoteToGraphic
函数用于输出Unicode Graphic字符而不是转义,但是这个函数甚至在标准库中都没有被使用 -
高效Quote字符串
当然,无论是
Quote
还是QuoteToASCII
亦或者QuoteToGraphic
都返回字符串从而需要重新分配内存,我们可以用下面三个函数替代func AppendQuote(dst []byte, s string) []byte func AppendQuoteToASCII(dst []byte, s string) []byte func AppendQuoteToGraphic(dst []byte, s string) []byte
-
Quote单独的rune
我们可以使用下面的函数来实现这个目的
func QuoteRune(r rune) string func QuoteRuneToASCII(r rune) string func QuoteRuneToGraphic(r rune) string
-
高效Quote单独的rune
func AppendQuoteRune(dst []byte, r rune) []byte func AppendQuoteRuneToASCII(dst []byte, r rune) []byte func AppendQuoteRuneToGraphic(dst []byte, r rune) []byte
-
Unquote字符串
我们可以使用
Unquote
来将quoted字符串转换为普通字符串,函数定义为func Unquote(s string) (string, error)
该函数不仅仅适用于
""
包含的字符串,还包括''
以及``包含的字符串 -
用困难的方式Unquote字符串
如果你希望让自己的生活更加艰难,你可以使用
UnquoteChar()
函数来转换字符串中的一个字符,函数定义如下func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error)
该函数会unquote第一个字符然后返回对应rune,如果该码点占用多个字符,则multibyte为true,tail返回剩下的字符串
结论
尽管fmt更加易于使用,但是fmt通常更慢也低效率。在基础类型和string之间转换我们应该优先使用strconv
包