GO标准库巡礼-strconv

作为初学者,我们可能常常首先接触到的是 fmt 包,但是对于基础类型的转换而言, strconv 包更加高效且允许编译器类型检查。

strconv 针对四种基础类型提供了相应的函数:布尔值,整数,浮点数和字符串。

整数操作

  • 如何从字符串中解析出有符号整数和无符号整数?

    我们可以使用 ParseIntParseUint 函数,它们的函数定义如下

        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字段为ErrSyntaxNumError

      另一个错误是解析的整数大于指定的bitSize。比方说解析"300"对于bitSize为8的时候就是非法的,因为int8最大值为127,此时会返回Err字段为ErrRangeNumError

    • 字符串解析为整数的简易函数?

      我们可以使用Atoi函数,函数定义如下

        func Atoi(s string) (int, error)
      

      在内部,该函数会调用ParseInt(s,10,0) ,注意Atoi返回的是int而ParseInt返回的是int64

      由于一般情况下都使用int(除非想要节省内存或者需要在32位系统上使用64位大小),所以通常我们可以用Atoi替代ParseInt

  • 如何将有/无符号整数转化为字符串
    我们可以使用FormatIntFormatUint函数,函数定义如下

        func FormatInt(i int64, base int) string
        func FormatUint(i uint64, base int) string
    

    这两个函数会首先将i转换为指定进制然后返回字符串表达,base支持2-36进制

    这两个函数在内部对于十进制以及2为底数的进制做了大量的优化。

    • 如何使用更少的内存分配来转换

      Format系列的函数最大的问题在于每次都会返回一个新的字符串,而内存分配是性能的杀手。为此我们可以改为使用Append系列函数,如AppendIntAppendUint函数

          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字段为ErrRangeNumError,同时结果为+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

发布了31 篇原创文章 · 获赞 32 · 访问量 729

猜你喜欢

转载自blog.csdn.net/a348752377/article/details/105050785