Go 知识点(12) — 类型转换以三方库 cast

类型转换在编程语言中是很常见的操作,在 Go 语言中其类型转换有下面一些注意点。

1. 整数类型之间的转换

对于整数类型转换,原则上目标类型的取值范围要包含被转换值,也就是说要转换类型的值取值范围要小于目标类型的取值范围。

如果相反,即目标类型小,而要转换的源值类型大,比如把值的类型从 int16 转换为 int8 ,会出现截断现象,如下代码:

func main() {
	src := int16(-255)
	dst := int8(src)
	fmt.Println("dst is ", dst) // dst is  1
}

变量 src 的值是 int16 类型的 -255 ,而变量 dst 的值是由前者转换而来的,类型是 int8int16 类型的可表示范围可比 int8 类型大。

整数在计算机中都是以补码的形式存储的。其中:

  • 正数补码和原码相同;
  • 负数补码是原码各位求反再加 1;

比如,int16 类型的值 -255 的补码是 1111111100000001

该值在转换为 int8 类型的值,那么就会把在较高位置(或者说最左边位置)上的 8 位二进制数直接截掉,从而得到 00000001 。又由于其最左边一位是 0 ,表示它是个正整数,以及正整数的补码就等于其原码,所以 dst 的值就是 1 。

规则:

  1. 当整数值的类型的有效范围由宽变窄时,只需在补码形式下截掉一定数量的高位二进制数即可;
  2. 当浮点数类型的值转换为整数类型值时,会把浮点数值的小数部分全部截掉;

2. 整数转换字符串

把整数转换为字符串,有两种方法可以使用,分别是:

  • 使用 string 直接转换;
  • 使用 strconv 库方法进行转换;

两者的区别,可以看下面代码:

func main() {
	a := 101
	s1 := string(a)
	s2 := strconv.Itoa(a)
	fmt.Printf("s1 is %#v, type is %T\n", s1, s1)
	fmt.Printf("s2 is %#v, type is %T\n", s2, s2)
}

输出结果:

s1 is "e", type is string
s2 is "101", type is string

我们可以看到区别,

  • string 方法是将整数转换为对应的 ASCII/Unicode 码值;
  • strconv 库的 Itoa 方法是将整数转换成整数字面量对应的字符串;

如下代码也是同样的

func main() {
	a := 0x597D
	s1 := string(a)
	s2 := strconv.Itoa(a)
	fmt.Printf("s1 is %#v, type is %T\n", s1, s1)
	fmt.Printf("s2 is %#v, type is %T\n", s2, s2)
}

输出结果:

s1 is "好", type is string
s2 is "22909", type is string

16 进制数 0x597D 的十进制数字为 22909 ,且其 Unicode 值为汉字的

但需要关注的是,被转换的整数值应该可以代表一个有效的 Unicode 代码点,否则转换的结果将会是 (仅由高亮的问号组成的字符串值)。

func main() {
	a := -2
	s1 := string(a)
	s2 := strconv.Itoa(a)
	fmt.Printf("s1 is %#v, type is %T\n", s1, s1)
	fmt.Printf("s2 is %#v, type is %T\n", s2, s2)
}

输出结果为

s1 is "�", type is string
s2 is "-2", type is string

由于 -2 无法代表一个有效的 Unicode 值,所以得到的总会是

3. string类型与各切片类型之间转换

一个值在从 string 类型向 []byte 类型转换时,除了与 ASCII 编码兼容的那部分字符集可以用单个字节表示之外,以 UTF-8 编码的字符串会被拆分成零散、独立的字节,单一字节是无法代表一个字符的。

func main() {
	a := "你好"
	s1 := string(a)
	s2 := []byte(a)
	s3 := []rune(a)
	fmt.Printf("s1 is %#v, type is %T\n", s1, s1)
	fmt.Printf("s2 is %#v, type is %T\n", s2, s2)
	fmt.Printf("s3 is %#v, type is %T\n", s3, s3)
}

输出结果为:

s1 is "你好", type is string
s2 is []byte{0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd}, type is []uint8
s3 is []int32{20320, 22909}, type is []int32

0xe4, 0xbd, 0xa0 合在一起才能代表字符 ,而 0xe5 , 0xa5 , 0xbd 合在一起才能代表符

一个值在从 string 类型向 []rune 类型转换时代表着字符串会被拆分成一个个 Unicode 字符。

[]int32{20320, 22909} // 你好 

4. 类型转换三方库

第三方包 github.com/spf13/cast 专门解决类型转换的问。这个包使用很简单,主要有两套函数:

  1. To_ 形式函数

这些函数始终返回所需的类型。如果无法正确转换为对应的类型,则返回目标类型的零值。

支持的类型包括所有的基本数据类型,还支持 time.Timetime.Durationslicemap 等常用类型。

比如:

cast.ToString("mayonegg")         // "mayonegg"
cast.ToString(8)                  // "8"
cast.ToString(8.31)               // "8.31"
cast.ToString([]byte("one time")) // "one time"
cast.ToString(nil)                // ""
cast.ToTime("2021-08-10 22:00:00") // 2021-08-10 22:00:00 +0000 UTC

注意,转换为 time.Time 时,需要注意时区问题。ToTime 默认使用 UTC,如果想用其他时区,得类似这么做:

secondsEastOfUTC := int((8 * time.Hour).Seconds())
beijing := time.FixedZone("Beijing Time", secondsEastOfUTC)
fmt.Println(cast.ToTimeInDefaultLocation("2021-08-10 22:00:00", beijing))

当然,你也可以这样:

fmt.Println(cast.ToTimeInDefaultLocation("2021-08-10 22:00:00", time.Local))

不过,Local 表示本地时区,要明确这个本地是不是你想要的。

  1. To_E 形式函数

E 表示 error,也就是说,这一系列函数会返回 error。在无法进行类型转换时,会将错误原因返回。To_ 形式内部调用的是 To_E 形似,只是它忽略了错误。

这种形式就不举例了。一般地,除非你需要区分零值是因为出错导致的还是本身就是零值,否则应该使用 To_ 系列函数,毕竟更省事。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值