今天开始更新go语言学习的内容,一方面可以记录自己的学习日常,还可以督促自己的学习。
本博客中大部分内容,均是看来《go圣经》之后的总结。
go语言中有四种数据类型:基础类型,复合类型,引用类型,接口类型。
基础类型包括数字,字符串,布尔类型。复合数据类型-数组,结构体,引用类型-指针,切片,字典,函数,管道。
整型
go语言中的数值类型包括几种大小不同的整数,浮点数和复数,每种数值类型都决定了对应的大小范围和是否支持正负号。
go语言提供了有符号和无符号的整数运算。int8 ,16,32,64四种不同大小的有符号整数类型,和uint8,16,32,64四种无符号的整数类型。
Unicode中的rune类型和int32是等价的类型,通常表示一个Unicode码点,byte是uint8的等价类型。
go语言中有符号数采用2进制补码的形式表示,最高位表示符号位。
%取模运算只能用于整数间的运算,在go语言中,%取模的结果符号和被取模数的符号相同。-5%3 = -2-5 %-3 = -2
/号依赖于操作数是否全为整数,5.0/4.0 = 1.255/4 = 1
如果运算结果溢出,则会丢弃溢出的部分。
虽然go提供了无符号数,但是大多数情况下还是倾向于使用有符号数,即使场景中没有负数的情况,例如len()函数,返回的是一个有符号的int,因为一般我们会使用len()>=0来进行循环判断,如果采用无符号数,则int永远不可能为-1,则循环永远不会结束。
在使用fmt.Printf进行打印的时候,%后面的[1]表示使用第一个参数,#表示会生成0,0x,0X前缀。
浮点数
go语言提供了两种类型的浮点数float32和float64,在math包中有它们表示的极限值,math.MaxFloat32等,通常使用float64,因为float32类型的累计计算误差容易扩散,并且float32能精确表示的正整数并不大(float32有效bit位只有23个,其他bit位用于指数和符号,当整数大于23bit的时候,float32将出现误差)
Printf中的%g可以打印浮点数,对于表格的数据,也可以采用%e,%f
math包中还包括了无穷大和无穷小,以及NaN用来表示无效的出发操作。
复数
go语言提供了两种精度的复数类型:complex64和complex128,分别对应于float32和float64,内置的complex函数用于构建复数,内建的real和imag函数用来返回复数的实部和虚部。
如果一个浮点数或者十进制整数后面跟着一个i,它将构成一个复数的虚部,实部为0。
复数的声明也可以简化: x := 1 + 2i
复数类型的比较需要实部和虚部都相等才相等。
布尔类型
布尔类型的值只有true和false,&&比||的优先级要高,在go语言中,并不会隐式的转化数字0和1
字符串
一个字符串是一个不可改变的字节序列。字符串可以包含任意的数据,通常是人类可读的文本。文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列。
内置的len()函数会返回字符串中的字节长度,注意这里不是返回rune序列长度。
索引s[i]用来返回第i个字节的字节值。
注意:第i个字节,不一定是字符串的第i个字符,因为对于非ASCII字符的UTF8编码可能需要两个或者三个字节。
+可以将两个字符串进行拼接
字符串可以进行比较,比较是通过逐个字符比较来完成的。
字符串的值是不可改变的:一个字符串包含的字节序列永远不会被改变,当然,我们可以给一个字符串变量分配一个新的字符串值,或者是进行拼接。但这并不会导致原始字符串的值被修改,因此,手动修改字符串的值是被禁止的。
字符串面值
字符串值也可以用字符串面值的方式编写,只需要将一系列字节序列包含在双引号即可
一个原生的字符串面值的形式是…
,使用反引号代替双引号。在原生的字符串面值中没有转义操作,全部内容都是字面意思。
Unicode编码:在很久以前,计算机世界中只有ASCII编码:美国信息交换标准代码,采用7bit来表示128个字符,对于早期的计算机是足够的,但是,随着计算机的发展,混合多种语言的数据变得常见,现在开始使用Unicode编码,它收集了世界上所有的符号系统,每个符号都分配一个唯一的Unicode码点,Unicode码点对应与go中的rune类型,是int32的等价类型。在第八版本的Unicode标准中收集了十二万个字符,对于每一个符文,表示为一个int32序列,这种编码方式叫做UTF-32,每个Unicode码点都使用同样大小的32bit位来表示,这种方式比较简单统一,但是会导致空间浪费的问题,因为常用的字符只需要大概16bit位就可以表示。
UTF-8
UTF8是一个将Unicode码点编码为字节序列的变长编码。UTF8编码使用1到4个字节来表示每一个Unicode码点,ASCII部分只是用一个字节,常用字符使用2到3个字节表示,每个符号编码后的第一个字节的高端bit位用来表示总共有多少个字节,如果第一个字节的高端bit是110,则说明需要两个字节,后续的每个高端bit都以10开头。
变长的编码无法使用索引来访问第n个字符,但是UTF8有很多额外的优点。首先UTF8编码比较紧凑,完全兼容ASCII,并且可以自动同步:它可以通过向前回溯最多两个字节就能确定当前字符编码的开始字节的位置。它也是一个前缀编码,所以可以从左向右解码。而且,没有任何字符的编码是其他字符编码的子串,因此搜索一个字符时只要搜索它的字节编码序列即可。
有很多Unicode字符很难从键盘输入,所以go提供了在字符面值中通过转义字符输入Unicode码点,有两种形式:\uhhhh对应16bit的码点值,\Uhhhhhhhh对应32bit的码点值,其中h是一个16进制的数字。
对于小于256码点值可以写在一个16进制转义字节中,例如\x41表示‘A’,但是对于更大的码点值,必须使用\u或者\U进行转义。
对于含有中英文混合的字符串时,如果我们关心的是每一个字符,可以考虑在读取字符的时候进行utf8解码,使用utf8.RuneCountInString()返回对应字符串的字符数量,使用utf8.DecodeRuneInString()来获取当前字符串的开头字符及其字节长度,但是这么做是笨拙的,在go中range循环会自动隐式的进行utf8解码,但是需要注意,在非ASCII字符中,i的增长可能不止加一。
当循环到错误的UFT8编码的时候,将生成一个特别的Unicode字符\uFFFD,当程序遇到这样的字符,说明输入出错了。
UTF8字符串作为交换格式是非常方便的,但是在程序内部采用rune序列更为方便,因为rune大小一致,支持数组索引和方便切割。
用字符串去初始化rune切片,将返回字符串编码的Unicode码点序列。
用rune切片或者是数组类型转化成string字符串,会进行Unicode编码。
对于一个整数,转化成string字符串的意思是转化成对应Unicode码点字符的字符串。
标准库中有四个包对字符串的处理尤为重要:bytes,strings,strconv,unicode包。
strings包提供了很多关于字符串查询,替换,比较,截断,拆分和合并等功能。
btyes包也提供了类型功能,但是针对和字符串有相同结构的[]byte类型,因为字符串是只读的,因此逐步构建字符串,会导致很多的分配和复制,在这种情况下,使用bytes.Buffer类型更加有效。
strconv包提供了布尔型,整型,浮点数和对应字符串的转化。
Unicode包提供了IsDigit,IsLetter,IsUpper等类似功能,用于给字符分类,每个函数都有一个单一的rune类型的参数,返回一个布尔值。像ToUpper之类的,用于rune字符的大小写转化。
将一个整数转化成字符串,一种方法是使用fmt.Sprint返回一个格式化的字符串,另一种方法是用strconv.Itoa方法,实现整数到ASCII的转化。
strconv中的FormatInt和FormatUint可以用不同的进制来格式化数字,返回一个不同进制的字符串。但是,使用fmt.Sprintf的%b,%d,%o,%x等参数有时更加的方便,Sprintf也是返回一个字符串。
如果将一个字符串转化成一个整数,可以使用strconv的Atoi或者是ParseInt函数,ParseInt函数中的第三个参数用来指定整型数的最大大小,在任何情况下,ParseInt都返回int64,但是,如果超出指定整型大小,会在error中报错。
常量
常量表达式的值在编译器计算,而不是在运行期。每种常量的潜在类型都是基础类型:boolean,string或数字。
一个常量的声明语句定义了常量的名字,和变量声明语法类似,常量的值不可被修改,这样可以防止在运行期被修改。常量比变量更适合用于表达像Π之类的数字常数。