【Go语言系列】2.4、Go语言基本程序结构:数据类型

Go 语言是一种静态类型的编程语言。这意味着,编译器需要在编译时知晓程序里每个值的类型。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。

  • Go语言内置以下这些基础类型:
  • 布尔类型:bool
  • 整型:int8、byte、int16、int、uint、uintptr等
  • 浮点类型:float32、float64。
  • 复数类型:complex64、complex128
  • 字符串:string
  • 字符类型:rune
  • 错误类型:error

此外,Go语言也支持以下这些复合类型:

  • 指针:pointer
  • 数组:array
  • 切片:slice
  • 字典:map
  • 通道:chan
  • 结构体:struct
  • 接口:interface

1、类型

1.1、布尔类型

布尔型的值只可以是常量 true 或者 false。例如:

var b bool = true

1.2、整型

序号长度(字节)类型与描述
1 1 uint8:无符号 8 位整型 (0 到 255)
2 2 uint16:无符号 16 位整型 (0 到 65535)
3 4 uint32:无符号 32 位整型 (0 到 4294967295)
4 8 uint64:无符号 64 位整型 (0 到 18446744073709551615)
5 1 int8:有符号 8 位整型 (-128 到 127)
6 2 int16:有符号 16 位整型 (-32768 到 32767)
7 4 int32:有符号 32 位整型 (-2147483648 到 2147483647)
8 8 int64:有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
9 1 byte:类似 uint8
10 4 rune:类似 int32
11 平台相关 uint:无符号的整型,32 或 64 位,
12 平台相关 int:有符号的整型,32 或 64 位,在Go语言中,int与int32是不同的数据类型,
编译器不会自动做数据转换
13 同指针 uintptr:无符号整型,用于存放一个指针,在32位平台下为4字节,64位平台下为8字节

例:

var value int32
value := 64 // value将会被自动推导为int类型

 

对整型数据可以进行数值运算、比较运算、位运算,详细在“运算符”这一节说明。

1.3、浮点型

浮点型用于表示包含小数点的数据,比如1.234就是一个浮点型数据。Go语言中的浮点类型采用IEEE-754标准的表达方式。

序号长度(字节)类型与描述
1 4 float32:单精度
2 8 float64:双精度

示例:

var fvalue1 float32
fvalue1 = 12 
fvalue2 := 12.0 // 如果不加小数点,fvalue2会被推导为整型而不是浮点型

1.4、复数型

这里就是我们数学中的复数,实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部(real),一个表示虚部(imag)。

示例:

var value1 complex64 // 由2个float32构成的复数类型
value1 = 3.2 + 12i 
value2 := 3.2 + 12i // value2是complex128类型
value3 := complex(3.2, 12) // value3结果同 value2

对于一个复数z = complex(x, y),就可以通过Go语言内置函数real(z)获得该复数的实部,也就是x,通过imag(z)获得该复数的虚部,也就是y。更多关于复数的函数,请查阅math/cmplx标准库的文档。

1.5、字符串

在Go语言中,字符串也是一种基本类型。一个字符串是一个不可改变的字节序列,字符串可以包含任意的数据,但是通常是用来包含可读的文本。

1.5.1、字符串定义

var str string // 声明一个字符串变量
str = "Hello world" // 字符串赋值

字符串中可以使用转义字符来实现换行、缩进等效果,常用的转义字符包括:

  • \n 换行符
  • \r 回车符
  • \t tab 键
  • \u 或 \U Unicode 字符
  • \\ 反斜杠自身

示例:

package main

import (
    "fmt"
)

func main() {
    var str = "Hello\nworld"
    fmt.Println(str)
}

运行结果:

Hello
world

多行字符串赋值

package main

import (
	"fmt"
)

func main() {
	var str = `第一行
第二行
    第三行\r\n
    第四行`
	fmt.Println(str)
}

运行结果

第一行
第二行
    第三行\r\n
    第四行

可以看出,在这种方式下,所有的转义字符均无效,文本将会原样输出。注意 ` 不是单引号,是反引号即键盘上 1 键左边的键。多行字符串一般用于内嵌源码和内嵌数据等。

1.5.2、字符串编码

Go语言中字符串的内部实现使用 UTF-8 编码,UTF-8 是一种被广泛使用的编码格式,是文本文件的标准编码,通过 rune 类型,可以方便地对每个 UTF-8 字符进行访问。当然,Go语言也支持按照传统的 ASCII 码方式逐字符进行访问。

1.5.3、字符串操作

运算含义示例
+ 字符串连接 "Hello" + "World" // 结果为HelloWorld
Len() 字符串长度 len("HelloWorld") // 结果为10
[] 取字符 "HelloWorld" [1] // 结果为'e'

1.5.4、字符串遍历

GO语言有两种遍历方法,一种是以字节数组的方式遍历:

package main

import "fmt"

func main() {
  str := "Hello World,你好世界" 
	n := len(str)
	for i := 0; i < n; i++ {
		ch := str[i] // 依据下标取字符串中的字符,类型为byte
		fmt.Println(i, ch)
	}

}

运行结果

0 72
1 101
2 108
3 108
4 111
5 32
6 87
7 111
8 114
9 108
10 100
11 44
12 228
13 189
14 160
15 229
16 165
17 189
18 228
19 184
20 150
21 231
22 149
23 140

在GO语言中汉字默认是UTF8编码占3个字节,字母和半角标点是ASCII编码占1个字节,所以len(str) == 24 ,输出的结果也是24行。

另一种是以Unicode字符遍历:

package main

import "fmt"

func main() {
	str := "Hello World,你好世界" 
	for i, ch := range str {
		fmt.Println(i, ch) //ch的类型为rune
	}

}

运行结果

0 72
1 101
2 108
3 108
4 111
5 32
6 87
7 111
8 114
9 108
10 100
11 44
12 20320
15 22909
18 19990
21 30028

在GO语言中Unicode字符占4个字节,故输出结果是16行。

1.5.5、字符串截取

本质上就是数组切片,这在后面的数组切片中会详细说明,这里只是举例:

package main

func main() {
	str := "Hello World,你好世界"
	newStr := str[1:3] //从第1个字节开始(含),读到取到第3个字节(含)
	println(newStr)
}

运行结果

el

需要留意的是这种字符串截取,实际是以字节的方式截取的,汉字占3个字节,可以尝试下面的代码看看结果如何

package main

func main() {
	str := "Hello World,你好世界"
	newStr := str[12:15]
	println(newStr)
}

1.5.6、字符串搜索

正向搜索首次出现的子字符串,示例:

package main

import "strings"

func main() {
	str := "Hello World,你好世界"
	pos := strings.Index(str, "世界")
	pos2 := strings.Index(str, "o")
	println(pos, pos2) //返回子字符串出现的位置
}

运行结果

18 4

 

反向搜索首次出现的子字符串,示例:

package main

import "strings"

func main() {
	str := "Hello World,你好世界"
	pos := strings.LastIndex(str, "好")
	pos2 := strings.LastIndex(str, "o")
	println(pos, pos2) //返回子字符串出现的位置
}

运行结果

15 7

我们可以看出在搜索子字符串时是以字节的方式遍历,汉字占3个字节

1.5.7、字符串拼接

在前面我们知道字符串拼接可以用+来处理,简单直观,但这种方式的性能并不好,因为每次将两个字符串拼接时实际上会生成一个全新的字符串。在GO语言中也有类似于 StringBuilder(可变字符串) 的机制来进行高效的字符串连接,示例:

package main

import (
	"bytes"
	"fmt"
)

func main() {
	firstString := "您好"
	secondString := "世界"

	// 声明字节缓冲
	var stringBuilder bytes.Buffer

	// 把字符串写入缓冲
	stringBuilder.WriteString(firstString)
	stringBuilder.WriteString(secondString)

	// 将缓冲以字符串形式输出
	fmt.Println(stringBuilder.String())
}

运行输出

您好世界

1.6、字符型

字符串中的每一个元素叫做“字符”,在遍历或者单个获取字符串元素时可以获得字符。字符(Character)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。

Go语言的字符有以下两种:

  • 一种是 uint8 类型,或者叫 byte(实际上是uint8的别名) 型,代表UTF-8字符串的单个字节的值,值对应ASCII 码。
  • 另一种是 rune 类型,代表一个Unicode字符,出于简化语言的考虑,Go语言的多数API都假设字符串为UTF-8编码,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。关于rune相关的操作,可查阅Go标准库的unicode包。另外unicode/utf8包也提供了UTF8和Unicode之间的转换。尽管Unicode字符在标准库中有支持,但实际上较少使用。

在 ASCII 码表中,A 的值是 65,使用 16 进制表示则为 41,所以下面的写法是等效的:

package main

func main() {
	//在 ASCII 码表中,A 的值是 65,使用 16 进制表示则为 41,所以下面的写法是等效的:
	var ch byte = 'A' //字符使用单引号括起来
	var ch2 byte = 65
	var ch3 byte = '\x41' //(\x 总是紧跟着长度为 2 的 16 进制数)
	var ch4 byte = '\101' //另外一种可能的写法是 \后面紧跟着长度为 3 的八进制数,例如 \377。
	println(ch, ch2, ch3, ch4)
}

在Go语言中,Unicode字符在内存中也是使用 int 来表示。在文档中,一般使用格式 U+hhhh 来表示,其中 h 表示一个 16 进制数,例如:

package main

import "fmt"

func main() {
  //在书写 Unicode 字符时,需要在 16 进制数之前加上前缀\u或者\U。因为 Unicode 至少占用 2 个字节,所以我们使用 int16 或者 int 类型来表示。如果需要使用到 4 字节,则使用\u前缀,如果需要使用到 8 个字节,则使用\U前缀。
	var ch int = '\u0041'
	var ch2 int = '\u03B2'
	var ch3 int = '\U00101234'
	fmt.Printf("%d - %d - %d\n", ch, ch2, ch3) // integer
	fmt.Printf("%c - %c - %c\n", ch, ch2, ch3) // character
	fmt.Printf("%X - %X - %X\n", ch, ch2, ch3) // UTF-8 bytes
	fmt.Printf("%U - %U - %U", ch, ch2, ch3)   // UTF-8 code point
}

运行结果:

65 - 946 - 1053236
A - β - 􁈴
41 - 3B2 - 101234
U+0041 - U+03B2 - U+101234

我们发现我们在代码中经常用fmt.Printf来向终端输出文字,在这个示例中用到了一些格式化说明符,它们的含义如下:

  • %c:用于表示字符,当和字符配合使用时。
  • %d:会输出用于表示该字符的整数,%v也是如此。
  • %X:表示16进制的整数
  • %U: 输出格式为 U+hhhh 的字符串。

Unicode 包中内置了一些用于测试字符的函数,这些函数的返回值都是一个布尔值,如下所示(其中 ch 代表字符):

package main

import "unicode"

func main() {
	// 	判断是否为字母:unicode.IsLetter(ch)
	// 判断是否为数字:unicode.IsDigit(ch)
	// 判断是否为空白符号:unicode.IsSpace(ch)
	println(unicode.IsLetter(65))

}

输出:

true

现在我们认识了什么是Go语言中的字符,在谈字符的时候常常还会谈到字符集。那么什么是字符集呢?顾名思义,字符集(Character set)是多个字符的集合

常见的字符集有:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。详细查阅:https://baike.baidu.com/item/字符集/946585?fr=aladdin

字符集为每个字符分配一个唯一的 ID,我们使用到的所有字符在 Unicode 字符集中都有一个唯一的 ID,例如上面例子中的 a 在 Unicode 与 ASCII 中的编码都是 97。

广义的 Unicode 指的是一个标准,它定义了字符集及编码规则,我们常用的UTF-8是Unicode的其中一个使用方式(编码)。 UTF是 Unicode Tranformation Format,即把Unicode转做某种格式的意思。

UTF-8便于不同的计算机之间使用网络传输不同语言和编码的文字,使得双字节的Unicode能够在现存的处理单字节的系统上正确传输。

UTF-8使用可变长度字节来储存 Unicode字符,例如ASCII字母继续使用1字节储存,重音文字、希腊字母或西里尔字母等使用2字节来储存,而常用的汉字就要使用3字节。辅助平面字符则使用4字节。

此外,还有数组(Array)切片(slice)指针(pointer)字典(map)通道(chan)接口(interface)错误(error)结构体(struct)等类型,这将在后面的章节再详细说明。

posted @ 2020-05-12 19:26  MrBug  阅读(580)  评论(0编辑  收藏  举报

人生就是一场战斗,唯有披荆斩棘,勇往直前!