strconv包可以大致分成string/数字型转换,rune/sting转换。常用的string/数字型转换又可以分三大类,Parse/Format/Append,简单理解就是数字型解析成字符串、数字型格式化成字符串和数字型拼接三种方法。
Parse
Parse系列函数
输入输出 ParseBools stringbool,error ParseInts string, 基数 int, 位数 intint64,error ParseUints string, 基数 int, 位数 intint64,error ParseFloats string, 位数 intfloat64, error ParseComplexs string, 位数 intcomplex128, error
其中ParseInt()/ParseUint()在类型转换一中有介绍过主要流程,其他流程大同小异。
ParseBool
将字符串转换成布尔类型,因为bool结果只有两个,需要转换的目标也有限,所以这个函数超简单,直接枚举长的像bool型的字符串,对应到bool结果上。不过“t”/”f”也能进行映射成true/false 是第一次见。
func ParseBool(str string) (bool, error) {
switch str {
case "1", "t", "T", "true", "TRUE", "True":
return true, nil
case "0", "f", "F", "false", "FALSE", "False":
return false, nil
}
return false, syntaxError("ParseBool", str)
}
ParseFloat
ParseFloat稍微复杂点,不过也是一样的套路:检查系统位数->检查极限字符->拆分字符串->遍历字符串
字符串“1.1”大致流程图
ParseFloat首先选择32/64位操作,32位转换和64位转换差别不大。以64位为例:具体使用内部函数atof64()里面有三个重要函数,分别是special()名如其功能,就是检查是否存在特殊情况比如无穷或非数,但它只能识别infinity/inf/nan;
readFloat()将字符串拆解成mantissa uint64 尾数数, exp int指, neg 是否负数, trunc 是否溢出, hex bool是否十六进制, i int 占用字节数, ok bool 转换是否成功。有了这些后面就可以直接处理了。
如果经过readFloat拆解是十六进制,则使用atofHex() 如果不是就可能使用atof64exact()和eiselLemire64算法,对于‘1.1’ atof64exact()就够了,atof64exact()也不复杂使用float64对尾数进行转换,对指数进行处理再与尾数相乘即可。
顺便看下16进制浮点数如何转换10进制浮点的,比如‘0x1a.2p1’ 0x是16进制标识,1a.2是尾数,p1就是e1。先将1a.2转换成二进制11010.001 指数是1小数点就向右移一位110100.01,再将2进制转换成10进制,110100=>52 .01 =>.25 52.25 atofHex()其实就是做的类似事情(ParseFloat()对16进制处理时候在readFloat()对p做了单独识别,而且p是判断16进制浮点数的条件。)
v := "1.1"
if s, err := strconv.ParseFloat(v, 32); err == nil {
fmt.Printf("%T, %v\n", s, s)
}
//float64, 1.100000023841858 #ParseFloat输出是float64所以按32位处理会影响精度
if s, err := strconv.ParseFloat(v, 64); err == nil {
fmt.Printf("%T, %v\n", s, s)
}
//float64, 1.1
v := "0xD.2p1"
if s, err := strconv.ParseFloat(v, 64); err == nil {
fmt.Print(s)
}
//26.25
ParseComplex
ParseXXX系列还有一个复数转换,复数不怎么用,常见用在几何或者物理方面。好在ParseComplex不复杂,基本就是parseFloatPrefix处理字符串,也就是ParseFloat()执行两次 。ParseComplex返回complex128的复数,需要注意是ParseComplex(s string, bitSize int) bitSize是complex的位数是64/128,函数默认是采用parseFloatPrefix(s,64)处理也就是atof64(s),所以处理complex64一定要指定bitSize对应目标位数,不然处理浮点型实虚数时会有精度缺失问题
v := "1.1+1i"
c1 ,_:= strconv.ParseComplex(v, 128)
fmt.Print(c1)
//(1.1+1i)
c2 ,_:= strconv.ParseComplex(v, 64)
fmt.Print(c2)
//(1.100000023841858+1i)
Format
strconv包里还有另一类函数,FormatXX 将数字转换成字符串
输入输出 FormatBoolb boolstring FormatInti int64, 基数 intstring FormatUinti uint64,基数 intstring FormatFloatf float64, fmt byte, prec, bitSize intstring FormatComplexc complex128, fmt byte, prec, bitSize intstring
FormatBool()很简单,就是直接返回“true/false”字符串;
func FormatBool(b bool) string {
if b {
return "true"
}
return "false"
}
FormatInt()和FormatUint()上次也学习过了Atoi
FormatFloat
FormatFloat(),需要四个参数f float64, fmt byte 格式参数(fmt必须是byte,所以用单引号), prec 小数保留位数(如-1是全保留), bitSize int基数
fmt可供选择:
// The format fmt is one of
// 'b' (-ddddp±ddd, a binary exponent), 二进制表达式
// 'e' (-d.dddde±dd, a decimal exponent), 十进制表达式
// 'E' (-d.ddddE±dd, a decimal exponent),
// 'f' (-ddd.dddd, no exponent), 普通的浮点表达式
// 'g' ('e' for large exponents, 'f' otherwise), 大数情况使用的标识
// 'G' ('E' for large exponents, 'f' otherwise),
// 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or 十六进制表达式
// 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent).
FormatFloat使用内部函数genericFtoa() ,genericFtoa()做的事情就是根据参数计算出原始float的十进制表达式,fmt是格式参数,指定计数格式,并且这个参数直接取决后续计算过程。
精度参数prec小数点位置(如果是-1默认全部长度),而且这时候使用Grisu3算法计算浮点数,据说是普通精度算法的四倍,具体如何计算暂时没有了解。
如果指定了精度长度就用普通的精度算法计算。篇幅限制就不展开genericFtoa()函数;
FormatComplex()和ParseComplex()一样,相当于执行两次FormatFloat()
var v float64
v = 3.1415926535
s64 := strconv.FormatFloat(v, 'E', 2, 64)
fmt.Printf("%T, %v\n", s64, s64)
//string, 3.14E+00
v:= (1.1+1i)
fmt.Print(strconv.FormatComplex(v,'f',-1,64))
//(1.1+1i)
Append
最后一类AppendXX函数,将数字类型进行拼接,需要注意不是计算,最终输出数组。因为最终拼接结果是字符串数组,所以拼接前需要和FormatXX一致的转换过程,如AppendFloat()底层处理逻辑就与FormatFloat()是相同逻辑
总结
- strconv包有三类函数 ParseXX 将字符串转换成数字类型;FormatXX 将数字类型转换成字符串 ;AppendXX 数字类型的拼接函数。这三类函数解决常见转换问题;
- 精度问题 strconv转换函数大多默认返回float64,会导致输入float32的结果精度失真,使用前应注意;
- strconv还有处理rune/ASCII/图形符号之间的转换QuoteXX系列,以及rune/图形判断函数,逻辑也都不复杂,可以直接使用;