布尔类型
布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换。
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类型来遍历,每次遍历都只遍历字符串中的1个byte,把1个byte赋值给变量i;第二种是每次遍历都遍历一个rune=4个byte赋值给变量char。
第二种把每个字符(不管是中文还是英文)都用4个byte的长度来存储,所以可以做到每次遍历都遍历到一个完整的中文字或英文字母。
由于byte类型是1个字节,rune是4个字节,所以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_byte和str_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 字符串类型