strconv包
定义了字符串和基本数据类型之间转换的方法,这⾥的基本数据类型包括:布尔、整型(包括有/⽆符号、⼆进制、⼋进制、⼗进制和⼗六进制)和浮点型等
strconv 包转换错误处理
由于将字符串转为其他数据类型可能会出错,strconv 包定义了两个 error 类型的变 量:ErrRange 和 ErrSyntax。其中,ErrRange 表示值超过了类型能表示的最⼤范 围,⽐如将 “128” 转为 int8 就会返回这个错误;ErrSyntax 表示语法错误,⽐如将 “” 转为 int 类型会返回这个错误。 然⽽,在返回错误的时候,不是直接将上⾯的变量值返回,⽽是通过构造⼀个 NumError 类型的 error 对象返回
// A NumError records a failed conversion.
type NumError struct {
Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
Num string // the input
Err error // the reason the conversion failed (ErrRange, ErrSyntax)
}
// 该结构不仅包含了⼀个error类型的成员,记录具体的错误信息
// ⽽且它⾃⼰也实现了error 接⼝
func (e *NumError) Error() string {
return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}
// 包的实现中,定义了4个便捷函数,⽤于构造 NumError 对象
func syntaxError(fn, str string) *NumError {
return &NumError{fn, str, ErrSyntax}
}
func rangeError(fn, str string) *NumError {
return &NumError{fn, str, ErrRange}
}
func baseError(fn, str string, base int) *NumError {
return &NumError{fn, str, errors.New("invalid base " + Itoa(base))}
}
func bitSizeError(fn, str string, bitSize int) *NumError {
return &NumError{fn, str, errors.New("invalid bit size " + Itoa(bitSize))}
}
字符串和整型之间的转换
字符串转为整型
包括三个函数:ParseInt、ParseUint 和 Atoi
func ParseInt(s string, base int, bitSize int) (i int64, err error)
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
func Atoi(s string) (i int, err error)
其中,Atoi 是 ParseInt 的便捷版,内部通过调⽤ ParseInt(s, 10, 0) 来实现的;ParseInt 转为有符号整型;ParseUint 转为⽆符号整型
参数 base 代表字符串按照给定的进制进⾏解释。⼀般的,base 的取值为2~36,如 果 base 的值为 0,则会根据字符串的前缀来确定。base 的值:”0x” 表示 16 进制;”0”表示 8 进制; 否则就是 10 进制。
参数 bitSize 表示的是整数取值范围,或者说整数的具体类型。取值。0、8、16、32 和64 分别代表 int、int8、int16、int32 和 int64
Go中,int/uint 类型,不同系统能表示的范围是不⼀样的,⽬前的实现是,32 位系统 占 4 个字节;64 位系统占 8 个字节。没有利⽤runtime.GOARCH之类的⽅式,⽽是巧妙的通过如下表达式确定 intSize
const intSize = 32 << uint(^uint(0)>>63)
const IntSize = intSize // number of bits in int, uint (32 or 64)
转换的基本原理(以 “128” 转 为 10 进制 int 为例)
// 在循环处理的过程中,会检查数据的有效性和是否越界等
s := "128"
n := 0
for i := 0; i < len(s); i++ {
n *= 10 + s[i] // base
}
整型转字符串
- func FormatUint(i uint64, base int) string // ⽆符号整型转字符串
- func FormatInt(i int64, base int) string // 有符号整型转字符串
- func Itoa(i int) string
其中,Itoa 内部直接调⽤ FormatInt(i, 10) 实现的。base参数可以取 2~36(0-9,a-z)
Go语言对base是10且i为100以内的数字的转化进行了优化,直接定义了两个基本常量字符串 进行获取,避免了100以为高频转化进行的开销
转化基本原理 (以 10 进制的 127 转 string 为例)
// 代表进制
const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
u := uint64(127)
var a [65]byte
i := len(a)
b := uint64(base)
for u >= b {
i--
a[i] = digits[uintptr(u%b)]
u /= b
}
i--
a[i] = digits[uintptr(u)]
return string(a[1:])
通常将数字转化为字符串fmt包中的函数也行
fmt.Sprintf("%d", 127)
但是fmt包中的方法传入的是interface结构,需要进行反射等操作,所以性能没有strconv包性能高
快速判断一个数字是否是2的整数幂
func isPowerOfTwo(x int) bool {
return x&(x-1) == 0
}
字符串和布尔值之间的转换
// 接受 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False 等字符串;
// 其他形式的字符串会返回错误
func ParseBool(str string) (value bool, err error)
// 直接返回 "true" 或 "false"
func FormatBool(b bool) string
// 将 "true" 或 "false" append 到 dst 中
// 这⾥⽤了⼀个 append 函数对于字符串的特殊形式:append(dst, "true"...)
func AppendBool(dst []byte, b bool)
输出 123true
fmt.Println(string(strconv.AppendBool([]byte("123"), true)))
字符串和浮点数之间的转换
func ParseFloat(s string, bitSize int) (f float64, err error)
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
func AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int)
prec 表示有效数字(对 fmt=’b’ ⽆效),对于 ‘e’, ‘E’ 和 ‘f’,有效数字⽤于⼩数点之后的位数; 对于 ‘g’ 和 ‘G’,则是所有的有效数字
strconv.FormatFloat(1223.13252, 'e', 3, 32) // 结果:1.223e+03
strconv.FormatFloat(1223.13252, 'g', 3, 32) // 结果:1.22e+03
由于浮点数有精度的问题,精度不⼀样,ParseFloat 和 FormatFloat 可能达不到互逆 的效果
s := strconv.FormatFloat(1234.5678, 'g', 6, 64)
strconv.ParseFloat(s, 64)
另外,fmt=’b’ 时,得到的字符串是⽆法通过ParseFloat还原的。特别地(不区分⼤⼩写),+inf/inf,+infinity/infinity,-inf/-infinity 和 nan 通过ParseFloat 转换分别返回对应的值(在 math 包中定义)。同样的,基于性能的考虑,应该使⽤ FormatFloat ⽽不是 fmt.Sprintf。
regexp - 正则表达式
Unicode只是定义了⼀个字符和⼀个编码的映射,但是呢,对应的存储却没有制定。 ⽐如⼀个编码0x0041代表⼤写字⺟A,那么可能有⼀种存储⾄少有4个字节,那可能 0x00000041来存储代表A。 这个就是unicode的具体实现。unicode的具体实现有很多 种,UTF-8和UTF-16就是其中两种。
UTF-8表示最少⽤⼀个字节就能表示⼀个字符的编码实现。它采取的⽅式是对不同的 语⾔使⽤不同的⽅法,将unicode编码按照这个⽅法进⾏转换。 我们只要记住最后的 结果是英⽂占⼀个字节,中⽂占三个字节。这种编码实现⽅式也是现在应⽤最为⼴泛 的⽅式了。
UTF-16表示最少⽤两个字节能表示⼀个字符的编码实现。同样是对unicode编码进⾏ 转换,它的结果是英⽂占⽤两个字节,中⽂占⽤两个或者四个字节。 实际上,UTF- 16就是最严格实现了unicode4.0。但由于英⽂是最通⽤的语⾔,所以推⼴程度没有 UTF-8那么普及。
- unicode
- unicode/utf8
- unicode/utf16
unicode包包含基本的字符判断函数。utf8包主要负责rune和byte之间的转换。utf16包 负责rune和uint16数组之间的转换。
由于字符的概念有的时候⽐较模糊,⽐如字符(⼩写a)普通显示为a,在重⾳字符中 (grave-accented)中显示为à。 这时候字符(character)的概念就有点不准确了, 因为a和à显然是两个不同的unicode编码,但是却代表同⼀个字符,所以引⼊了 rune。 ⼀个rune就代表⼀个unicode编码,所以上⾯的a和à是两个不同的rune。 这⾥有个要注意的事情,go语⾔的所有代码都是UTF8的,所以如果我们在程序中的 字符串都是utf8编码的,但是我们的单个字符(单引号扩起来的)却是unicode的。
unicode包
func IsControl(r rune) bool // 是否控制字符
func IsDigit(r rune) bool // 是否阿拉伯数字字符,即1-9
func IsGraphic(r rune) bool // 是否图形字符
func IsLetter(r rune) bool // 是否字⺟
func IsLower(r rune) bool // 是否⼩写字符
func IsMark(r rune) bool // 是否符号字符
func IsNumber(r rune) bool // 是否数字字符,⽐如罗⻢数字Ⅷ也是数字字符
func IsOneOf(ranges []*RangeTable, r rune) bool // 是否是RangeTable中的⼀个
func IsPrint(r rune) bool // 是否可打印字符
func IsPunct(r rune) bool // 是否标点符号
func IsSpace(r rune) bool // 是否空格
func IsSymbol(r rune) bool // 是否符号字符
func IsTitle(r rune) bool // 是否title case
func IsUpper(r rune) bool // 是否⼤写字符
utf8包
utf8⾥⾯的函数就有⼀些字节和字符的转换。
判断是否符合utf8编码的函数:
- func Valid(p []byte) bool
- func ValidRune(r rune) bool
- func ValidString(s string) bool
判断rune的⻓度的函数:
- func RuneLen(r rune) int
判断字节串或者字符串的rune数:
- func RuneCount(p []byte) int
- func RuneCountInString(s string) (n int)
编码和解码rune到byte:
- func DecodeRune(p []byte) (r rune, size int)
- func EncodeRune(p []byte, r rune) int
utf16包
将int16和rune进⾏转换:
- func Decode(s []uint16) []rune
- func Encode(s []rune) []uint16
unicode有个基本字符平⾯和增补平⾯的概念,基本字符平⾯只有65535个字符,增补 平⾯(有16个)加上去就能表示1114112个字符。 utf16就是严格实现了unicode的这 种编码规范。
⽽基本字符和增补平⾯字符就是⼀个代理对(Surrogate Pair)。⼀个代理对可以和⼀ 个rune进⾏转换
- func DecodeRune(r1, r2 rune) rune
- func EncodeRune(r rune) (r1, r2 rune)