Go入门系列(三) 基础类型——整型、浮点型、布尔类型和字符串-张柏沛IT博客

正文内容

Go入门系列(三) 基础类型——整型、浮点型、布尔类型和字符串

栏目:Go语言 系列:Go入门系列 发布时间:2021-01-04 09:28 浏览量:303

布尔类型

布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换。

var b bool

b = 1 // 编译错误

b = bool (1) // 编译错误

 

以下的用法才是正确的:

var b bool

b = (1!=0) // 编译正确

fmt.Println("Result:", b) // 打印结果为Result: true

 

整型

类型

长度(字节)

值范围

int8

1

-128 ~ 127

uint

1

0~255

int16

2

-32768 ~ 32767

uint16

2

0 ~ 65 53

int32

4

-2147483648 ~ 2147483 64

uint32

4

0 ~ 4 294 967 2

int64

8

...

uint64

8

...

int

平台相关

平台相关

uint

平台相关

平台相关

uintptr

在32位平台下为4字节,64位平台下为8字节

 

     

 

需要注意的是,int/int8/16/32/64在Go语言里被认为是5种不同的类型,编译器也不会帮你自动做类型转换,比如以下的例子会有编译错误:

var value2 int32
value1 := 64	// value1将会被自动推导为int类型而不是int8/16/32/64
value2 = value1 // 编译错误,因为二者是不同类型
// 如果是 value2 = 1 然后 value1 = value2 这样不会报错,因为int类型兼容int8/16/32/64。但是int8/16/32/64不兼容int类型。

 

编译错误类似于:

cannot use value1 (type int) as type int32 in assignment

 

同样的将一个int32的变量赋值给int64的变量也会报错。

 

使用强制类型转换可以解决这个编译错误:

value2 = int32(value1)   // 编译通过

 

当然,开发者在做强制类型转换时,需要注意数据长度被截短而发生的数据精度损失(比如将浮点数强制转为整数)和值溢出(值超过转换的目标类型的值范围时,比如int64转为int32)问题。

两个不同类型的整型数不能直接比较,比如int8类型的数和int类型的数不能直接比较。但是各种类型的整型变量都可以直接与字面常量(literal)进行比较,比如:

var i int32
var j int64
i, j = 1, 2
if i == j {	//编译错误
	fmt.Println("i and j are equal.")
}

if i == 1 || j == 2 { // 1和2就是字面常量
	fmt.Println("i and j are equal.")
}

 

浮点型

定义一个浮点型有两种方法:

var fvalue1 float32

fvalue1 = 12

 

或者

 

fvalue2 := 12.0   // 自动设为float64

 

如果

fvalue1 = fvalue2     // 报错,因为不能将float64的变量赋值给float32的变量

 

而必须使用这样的强制类型转换: fvalue1 = float32 (fvalue2)

 

因为浮点数不是一种精确的表达方式,所以像整型那样直接用==来判断两个浮点数是否相等是不可行的,这可能会导致不稳定的结果。下面是一种推荐的替代方案:

package main

import (
	"fmt"
	"math"
)

func main(){
	var a float64 = 10.00000000013
	var b float64 = 10.00000000014
	fmt.Println(isEqual(a, b, 0.0000001))
	//fmt.Println(a > b)
}

func isEqual(f1, f2, p float64) bool {
	var diff float64
	if f1 > f2 {
		diff = math.Dim(f1, f2)
	}else{
		diff = math.Dim(f2, f1)
	}

	return diff < p
}

math.Dim(x, y)方法返回 x-y和0的最大值,比如

fmt.Println(math.Dim(1000,2000))    //0

fmt.Println(math.Dim(1000,200))    //800

 

但是实际上直接浮点型进行 < > ==的比较也都还行,基本不会出错。

 

字符串

用一个例子介绍字符串的基本使用方法

package main

import "fmt"

func main(){
	str1 := "Hello 世界"
	firstAlpha, strLen := showStr(str1)

	fmt.Printf("字符串str1的内容为 '%s', 第一个字节为 %c, 长度为 %d", str1, firstAlpha, strLen)
}

func showStr(str string) (firstAlpha uint8, strLen int) {
	firstAlpha = str[0]
	strLen = len(str)
	return firstAlpha, strLen
}

上面的例子说明了,字符串可以用下标的方式访问某一个字节(而不是字符),而且这个字节是一个uint8的类型(其实就是byte类型),而不是一个字符串类型。

 

所以要用 %c 来占位firstAlpha,而不是用%s占位firstAlpha。%c表示以字符形式输出,%s表示以字符串形式输出。

 

还有就是不要写为:

func showStr(str string) (firstAlpha uint8, strLen int) {
	firstAlpha := str[0]
	strLen := len(str)
	return firstAlpha, strLen
}

因为firstAlpha和strLen已经在函数声明返回值列表的时候声明了,不能用:=重复声明,而是用 = 直接赋值,否则会报错。

 

字符串的内容可以用类似于数组下标的方式获取,但不能用下标的方式修改字符串中的内容:

str := "Hello world"     // 字符串也支持声明时进行初始化的做法

str[0] = 'X'  // 编译错误

 

编译器会报类似如下的错误:

cannot assign to str[0]

 

字符串连接:

x + y

 

字符串遍历:

Go语言支持两种方式遍历字符串。一种是以字节数组的方式遍历:

package main

import "fmt"

func main() {

    str := "hello word 你好世界"

    length := len(str)

    for i := 0; i < length; i++ {

        fmt.Println(str[i])

    }

}

 

结果为
0 104
1 101
...
21 149
22 140
 

另一种是以Unicode字符遍历

package main

import "fmt"

func main() {
	str := "hello word 你好世界"
	for i, chr := range str {
		fmt.Println(i, chr)
	}
}

 

结果为:
0 104
1 101
...
10 32
11 20320
14 22909
17 19990
20 30028
 

在Go语言中支持两种字符类型,一种是byte,一种是rune。我们进入Go的源码可以看到,byte的本质是uint8的整型,rune的本质是int32的整型,byte是1一个字节,rune是4个字节(rune和byte本质都是整型,都是个数字,只不过是长度不同的数字)。

上面的两种遍历方式,第一种是按byte类型来遍历,每次遍历都只遍历字符串中的1byte,把1byte赋值给变量i;第二种是每次遍历都遍历一个rune=4byte赋值给变量char

第二种把每个字符(不管是中文还是英文)都用4byte的长度来存储,所以可以做到每次遍历都遍历到一个完整的中文字或英文字母。

由于byte类型是1个字节,rune4个字节,所以byte叫做字节类型,rune叫做字符类型,string叫做字符串类型。

 

当我们使用下标方式(str[0] )读取字符串的一个字节的时候,返回的是一个byte类型。

当我们使用字符串切片(str[1:4)读取字符串的多个字节时,返回的是一个string类型。

 

如果我们想将一个字符串强制转为byte类型或者rune类型,以下做法是错误的:

str := "Hello World! 你好世界!"

str_byte := byte(str)         // 报错

str_rune := rune(str)        // 报错

原因很简单,str的长度肯定不止一个字节或者4个字节,怎么能用byte()或者rune()把一个字符串直接转成一个字节或者一个4字节的rune字符呢。

 

就算是单字符的字符串直接用byte()或者rune()转成byte或者rune类型也会报错。

str_byte := byte("C")       // 报错

str_rune := rune("C")       // 报错

// 但是 str_byte := byte(‘c’) 是可以的,因为 ‘c’不是字符串型而是一个整型

 

其实我们应该将字符串转为byte类型的切片或者rune类型的切片才对

str := "Hello World! 你好世界!"

str_byte := []byte(str)

str_rune := []rune(str)

 

此时我们尝试对str_bytestr_rune这两个切片进行遍历,遍历的结果分别和上面第一种遍历方式和第二种遍历方式一样。

 

现在我有一个需求,我想判断一个url是否为http协议的url

url := "https://www.zbpblog.com"

if url[:5] == "http:" {

    fmt.Println("OK")

}

 

这样是没错的,因为 url[:5] 返回的也是一个字符串,与”http”是同一类型,可以比较。

如果我想判断一个字符串的第一个字母是不是a

str := "abcd"

println(str[0] == "a")                 // 报错,因为 str[0]byte类型而 “a”是字符串类型。

 

正确的做法是

str := "abcd"

println(str[0] == 'a')

 

这里单引号的a就不是一个字符串类型,而是一个int32类型。那么str[0]是一个byte类型,而’a’是一个int32类型,按理说二者不是一个类型,比较会报错才对呀!这里其实因为’a’是一个整型字面值,所以可以和任何整型类型进行比较。像类似于 100 == ‘a’, rune(str[0]) == ‘a’ 也是可以比较的。

但是如果我们强行把 ‘a’转为rune类型,那么 str[0] == rune(‘a’) 就真的是两种不同类型比较,会报错。

 

那么我们如果不想遍历得到数字,而是遍历string类型的字符,可以使用第二种遍历方式 + string强制转换rune类型的方式做到

package main

import "fmt"

func main() {
	str := "hello word 你好世界"
	for i, chr := range str {
		fmt.Println(i, string(chr))
	}
}

 

如果我希望将一个数字200转为字符串"200"请问要怎么做,估计很多新手会用string()强制转换。但是结果却是:

fmt.Println(string(200))     // È

 

原因是 string() 把200这个数字当作是一个ASCII码来处理,这样Go就会在ASCII码映射表中找200对应的字符,找到的就是 È。在上面的例子中,string(chr)其实做的就是这件事,只不过chr是一个32位4字节的整型,所以go不再是从ASCII码映射表中找对应字符(因为ASCII表的存的是0~255范围的字符,字符全都是以1个字节长度来存的),而是从Unicode编码映射表中找对应的字符。

正确的做法应该是:

res = strconv.Itoa(200))      // "200"

或者

res = fmt.Sprintf("%s", 200)                           //    字符串格式化的方式来转

另外string()也可以将[]byte或者[]rune或者单个byte字节或者rune字符转为字符串。

 

像上面的go代码中有中文的,就必须用utf-8来编码这个go文件,否则可能报错。Go语言仅支持UTF-8和Unicode编码。对于其他编码,Go语言标 准库并没有内置的编码转 换支持。不过,所幸的是我们可以很容易基于iconv库用Cgo包装一个。

 

另外,我们可以通过``的方式定义一个多行字符串。

 

小结:byte 字节类型    rune 字符类型    string 字符串类型

 

 

 

如果您需要转载,可以点击下方按钮可以进行复制粘贴;本站博客文章为原创,请转载时注明以下信息

张柏沛IT技术博客 > Go入门系列(三) 基础类型——整型、浮点型、布尔类型和字符串

热门推荐
推荐新闻