ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage
Edit page

03 基本数据类型的类型转换

基本数据类型的类型转换

Go默认是不会进行数据类型的隐士转换的(即不能自动进行数据转换),需要我们显示指定数据类型转换。

1 兄弟类型转换

类型转换的基本语法:TYPE(var), TYPE表示数据类型,var表示要进行类型转换的变量。例如int32(a)

兄弟类型转换使用细节:

  • TYPE(var)这种方式只适合兄弟类型的转换(数字类型之间转换:int8 int32 float之间的转换),不适合大类型之间转换(比如int32 与string之间的转换)。

  • 不是所有数据类型都能转换的,例如字母格式的string类型"abcd"转换为int肯定会失败

  • 被转换的是变量存储的数据(即值),变量本身的数据类型没有发生变化。

  • 在(范围)大转小的转换中,比如将int64转换为int8,编译时不会报错,只是转换的结果是按照溢出处理,和我们希望的结果不一样。

  • 低精度转换为高精度时是安全的,高精度的值转换为低精度时会丢失精度。例如float32转换为int

2 大类型转换-基本数据类型转string类型

**方式1:使用fmt.Sprintf("%参数",表达式)函数, 根据于格式说明符进行格式化并返回其结果字符串。 **

func main() {
	var num1 int = 99
	var num2 float32 = 23.456
	var b bool = true
	var char byte = 'h'
	var str1, str2, str3, str4 string

	str1 = fmt.Sprintf("%d", num1)
	str2 = fmt.Sprintf("%f", num2)
	str3 = fmt.Sprintf("%t", b)
	str4 = fmt.Sprintf("%c", char)

	fmt.Println(str1) // 99
	fmt.Println(str2) // 23.455999
	fmt.Println(str3) // true
	fmt.Println(str4) // h
}

方式2:使用strconv.FormatXXX()系列函数

使用strconv包中的Format类函数进行转换,详见下文。

3 大类型转换-string转基本数据类型

使用strconv包中的Parse类函数进行转换,详见下文。

4 strconv包

strconv包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换。

这个包里提供了很多函数,大概分为几类:

  • 字符串转int:Atoi()

  • int转字符串: Itoa()

  • ParseTYPE类函数将string转换为TYPE类型:ParseBool()、ParseFloat()、ParseInt()、ParseUint()。因为string转其它类型可能会失败,所以这些函数都有第二个返回值表示是否转换成功

  • FormatTYPE类函数将其它类型TYPE转string类型:FormatBool()、FormatFloat()、FormatInt()、FormatUint()

  • AppendTYPE类函数用于将TYPE转换成字符串后append到一个slice中:AppendBool()、AppendFloat()、AppendInt()、AppendUint()

还有其他一些基本用不上的函数,见官方手册:go doc strconv或者https://golang.org/pkg/strconv/。

当有些类型无法转换时,将报错,返回的错误是strconv包中自行定义的error类型。有两种错误:

var ErrRange = errors.New(“value out of range”) var ErrSyntax = errors.New(“invalid syntax”)

例如,使用Atoi(“a”)将"a"转换为int类型,自然是不成功的。如果print输出err信息,将显示:

strconv.Atoi: parsing “a”: invalid syntax

4.1 string和int的相互转换

  • int转换为字符串:Itoa()
println("a" + strconv.Itoa(32))  // a32
  • string转换为int:Atoi(),2个返回值

由于string可能无法转换为int(比如非形似数字的字符串"abc"),所以这个函数有两个返回值:第一个返回值是转换成int的值,第二个返回值判断是否转换成功。

func main() {
	i, _ := strconv.Atoi("3")
	println(3 + i) // 6

	// Atoi()转换失败
	i, err := strconv.Atoi("a")
	if err != nil {
		println("converted failed")
	}
}

4.2 ParseTYPE类函数(字符串解析)

​ Parse类函数用于转换字符串为给定类型的值:ParseBool()、ParseFloat()、ParseInt()、ParseUint()。由于字符串转换为其它类型可能会失败,所以这些函数都有两个返回值,第一个返回值保存转换后的值,第二个返回值判断是否转换成功。

此外需要主要的是 ,要确保striing类型能够转成有效的数据。例如我们可以把"123"转换为int,但是不能把"hello"转换成int,这种情况下,Golang会直接将其转换默认零值,int型会转换为0。

示例:

b, err := strconv.ParseBool("true")
f, err := strconv.ParseFloat("3.1415", 64)  // 将string转换为float64的浮点型
i, err := strconv.ParseInt("-42", 10, 64)   // 将string使用10进制解析,然后转换为int64的整形
u, err := strconv.ParseUint("42", 10, 64)   // 同上

参数说明:

对于解析为整数的方法ParseInt()、ParseUint()有3个参数。

func ParseInt(s string, base int, bitSize int) (i int64, err error)
func ParseUint(s string, base int, bitSize int) (uint64, error)
  • base参数表示以什么进制的方式去解析给定的字符串,有效值为0、2-36(最大36进制=10个数字+26个字母)。当base=0的时候,表示根据string的前缀来判断以什么进制去解析:0x开头的以16进制的方式去解析,0开头的以8进制方式去解析,其它的以10进制方式解析。
  • bitSize参数表示以intXX数据类型的方式去存储解析后的整数,有效值为0、8、16、32、64。当bitSize=0的时候,表示以int或uint类型。例如bitSize=8表示以类型为int8或uint8去解析string。如果给定的string表示的整数值超过bitSize指定的类型的表示范围,则会返回其边界值。例如当bitSize为8时,int8的范围是-128~127,如果给定的string为"128",则会返回127;如果给定的string为"-300",则会返回-128。
  • 返回值,无论指定bitSize为多少,最终会将返回值由bitSize类型转换为指定的返回值类型。所以为了避免转换过程中的范围溢出现象,同时结合方法返回值类型,一般直接将bitSize直接设置为64。

bitSize范围溢出示例:

func main() {
	var str1 = "120"
	n1, _ := strconv.ParseInt(str1, 10, 8)
	fmt.Printf("%d\t%T\n", n1, n1) // 120   int64

	var str2 = "128"
	n2, _ := strconv.ParseInt(str2, 10, 8)
	// 范围溢出:128使用int8保存后溢出,然后将溢出后的int8转换为int64后返回127
	fmt.Printf("%d\t%T\n", n2, n2) // 127   int64

	var str3 = "-300"
	n3, _ := strconv.ParseInt(str3, 10, 8)
	//-300使用int8保存后溢出,然后将溢出后的int8转换为int64后返回-128
	fmt.Printf("%d\t%T\n", n3, n3) // -128  int64  
}

避免范围溢出最佳实践:

ParseInt()、ParseUint()解析过程:string–>使用base进制解析字符串,然后转换成bitSize格式,最终将bitSize类型转换为方法的返回值类型(int64),最终的方法返回的是int64类型,所以为了避免转换过程中的范围溢出现象,结合方法返回值类型,一般直接将bitSize直接设置为64。

func main() {
    // 以10进制方式解析"-42",保存为int64类型:
	i1, _ := strconv.ParseInt("-42", 10, 64)
	fmt.Println(i1)  // -42
	// 以5进制方式解析"23",保存为int64类型:
	i2, _ := strconv.ParseInt("23", 5, 64)
	println(i2) // 13  因为5进制的时候,23表示进位了2次,再加3,所以对应的十进制数为5*2+3=13。
	// 以16进制解析23,保存为int64类型:
	i3, _ := strconv.ParseInt("23", 16, 64)
	println(i3)    // 35  因为16进制的时候,23表示进位了2次,再加3,所以对应的十进制数为16*2+3=35。
	// 以15进制解析23,保存为int64类型:
	i4, _ := strconv.ParseInt("23", 15, 64)
	println(i4)    // 33 因为15进制的时候,23表示进位了2次,再加3,所以对应的十进制数为15*2+3=33。
}

4.3 FormatTYPE类函数(格式化成字符串)

将给定类型格式化为string类型:FormatBool()、FormatFloat()、FormatInt()、FormatUint()。

示例:

s := strconv.FormatBool(true)  					//true
s := strconv.FormatFloat(3.1415, 'E', -1, 64) 	// 3.1415E+00
s := strconv.FormatInt(-42, 16) 				// -2a
s := strconv.FormatUint(42, 16)               	// 2a

FormatInt()和FormatUint()有两个参数:

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

第二个参数base指定将第一个参数转换为多少进制,有效值为2<=base<=36。当指定的进制位大于10的时候,超出10的数值以a-z字母表示。例如16进制时,10-15的数字分别使用a-f表示,17进制时,10-16的数值分别使用a-g表示。例如:**FormatInt(-42, 16)**表示将-42转换为16进制数,转换的结果为-2a。

FormatFloat()参数:

func FormatFloat(f float64, fmt byte, prec, bitSize int) string

fmt表示输出格式;

prec表示小数位数;

bitSize参数表示转换为多少位(32或64)的浮点数对应的字符串;

s := strconv.FormatFloat(3.1415, 'f', 6, 64) // 'f'表示输出格式;6表示小数位数;64表示转换为64位的浮点数对应的字符串
println(s) // 3.141500

4.4 AppendTYPE类函数(格式化成slice)

​ AppendTYPE类函数用于将TP转换成字符串后append到一个slice中:AppendBool()、AppendFloat()、AppendInt()、AppendUint()。Append类的函数和Format类的函数工作方式类似,只不过是将转换后的结果追加到一个slice中。

func main() {
	// 声明一个slice
	b10 := []byte("int (base 10):")

	// 将转换为10进制的string,追加到slice中
	b10 = strconv.AppendInt(b10, -42, 10)
	fmt.Println(string(b10)) // int (base 10):-42

	b16 := []byte("int (base 16):")
	b16 = strconv.AppendInt(b16, -42, 16)
	fmt.Println(string(b16)) // int (base 16):-2a
}