Go入门笔记。有很多东西还没有涉及。仅供参考。
求Go大佬指点。
安装(Sublime Text 3)
-
官网下载最新版Go并安装。同时安装好Sublime Text 3与Sublime插件Package Control。
-
使用Sublime插件Package Control安装插件Golang Build和GoSublime。
-
用
.go
文件存储源代码。注意每个文件夹内的Go文件是自动相互关联的,即所有文件会被视作一个整体,编译产生的可执行程序名称为文件夹名称。用Sublime打开后可以Ctrl + Shift + B
并选择编译选项。 -
GoSublime
选项支持打开一个终端。用go help
查看Go语言的终端命令。注意Sublime进行Go语言程序的运行都不能输入。
基本情况
1 | package main |
Hello World程序范例。
-
空格不区别,文末回车不区别,左大括号强制不换行。
-
使用
import "xxx"
引入模块,可以一次引入多个模块形如import("xxx","yyy","zzz")
。引用没有使用到的模块、声明没有使用的变量、废弃的返回值会导致编译报错。Go语言也支持直接从Github或者网络获取模块。 -
区分大小写,变量名只能以下划线和字母开头,不能以数字开头,不能包含空格(同C++)。变量名不能包含下述字符:
~ ! @ # $ % ^ & * ( ) ; - : " ' < > , . ? / { } [ ] + = /
-
//
进行单行注释,/**/
进行多行注释。Go语言的许多语法与C和C++几乎完全一致。
输入输出
- 输入输出包含在
fmt
模块中。
Println
输出并换行。逗号连接多个参数等价于空格作为分隔符输出。
输出。逗号连接多个参数等价于空格作为分隔符输出。
Printf
输出。使用方式和C++几乎完全一致。对于格式化字符串,新增%t
表示布尔类型(输出true
或false
),%v
表示通用类型(包括自定义结构体),%+v
输出自定义结构体时会输出属性名称,%#v
输出自定义结构体新建时的Go语法,%T
输出类型,形如%[n]v
指示输出第n个提供的参数(从1开始),%q
输出未经转义的字符串。
Scanln
输入一行。Scanln(&a,&b,&c,...)
直接将输入以空格作为分隔符赋值给各个变量。
Scan
输入。Scan(&a,&b,&c,...)
直接将输入以空格或回车作为分隔符赋值给各个变量。
Scanf
输入。使用方式和C++几乎完全一致。可以用形如%5s
表示输入5个字符,用%v
以空格作为分隔符输入通用类型的各个参数。
数据类型
-
变量声明
var NAME TYPE
。或者数组形如var NAME [n]TYPE
。变量总是会被默认初始化为0。常量定义形如const NAME TYPE = xxx
。省略TYPE
会导致推断。 -
区别于
=
进行赋值,用:=
表示声明并赋值。类似于Python支持多个变量的赋值与声明,相应地支持一个函数的多返回值。 -
各种不同的声明方式:
1 | var ( |
-
用形如
A{3,3.5,"aaa",7}
方式新建一个结构体对象。 -
iota
是一个从0开始的自增量:
1 | var ( |
就会得到a,b,c,d
分别是0,1,2,3
。
int
类型
支持^ ! * / % << >> & &^ + - | ^ == != < <= > >= && || = += -= *= /= %=
和C类似(运算符优先级从左到右)。其中高优先级的^
是单目运算符表示C中的~
,而优先级的则表示异或。a &^ b
表示a&(~b)
。++
和--
只能作为独立语句,不能再作为表达式的一部分。
支持(u)int__
的定义方式,__
可以是8
,16
,32
,64
。注意类型之间不能自动转换,单独的声明(u)int
类型根据机器字长而变化,是不可移植的。
float
类型
支持float32
和float64
。
complex
类型
支持complex64
和complex128
。
Slice
类型和数组
形如A := []int32{1,2,3}
声明一个数组。多维数组只有第一维可以省略。
也可以使用A := make([]TYPE,CUR_SIZE,MAX_SIZE)
来声明一个长度为MAX_SIZE
的切片A
,其中前CUR_SIZE
个元素被初始化了。
Go语言中的数组是一个值!与此相对,切片是一个引用,这与C++和Python都恰好相反。
类似于Python可以使用a[l:r]
可以获得一个子列表包含a
中[l,r)
的元素,即一个切片(Slice)。可以省略l
或r
甚至两个都省略,表示一直延伸到两端。
1 | package main |
会发现a,b,c
都改变了而d
没有改变。
append(A,x,y,...)
: 在A
切片后面追加x
(CUR_SIZE
后面追加,超过MAX_SIZE
后会重新分配空间),返回值是新的切片,注意原切片不变(可以视作是一对L,R
),而数组改变了。
string
类型
必须用双引号定义。本质是一个byte
数组。byte
类型的本质是uint8
,可以说两者等价。
单引号定义得到字符类型,是一个rune
类型。rune
类型的本质是int32
,可以说两者等价,但是rune
被用于表示字符。
用英文的`符号可以定义原生字符串。
字符串的默认值是""
,而不是nil
。
支持!= == < > + +=
。
字符串本身是不可直接修改的,需要强行转换为[]byte
或[]rune
。
map
类型
通过M := make(map[KEY_TYPE]VAL_TYPE)
声明,基本使用方法和map
几乎一致。KEY_TYPE
需要定义==
、!=
,map
自动按照关键字顺序排序。默认返回值是0
。
delete(M,key)
来删除一个元素。
迭代期间的增删是安全,但是并发程序同时读写或写写map
会导致报错。
结构语法
结构语法和C++几乎完全相同,if
和for
等的实现不需要大括号。
if
if
的基本用法例如:
1 | if EXPRESSION { |
switch
1 | switch EXPRESSION { |
与C++不同的是去除了每个case
后面需要break
的要求。相反用fallthrough
来跳过默认的break
。
for
for
的基本用法例如:
1 | for i := 0; i < n; i++ { |
而for
完全取代了while
,例如:
1 | for EXPRESSION { |
甚至可以表示while(true)
,例如:
1 | for { |
可以用for-range
来访问一个数组(切片)。range
得到的对象是一个二元组i,A[i]
。注意如果定义了i
却不使用是会编译错误的,用_
代替可以避免报错。例如数组求和:
1 | s := int32(0) |
这里用range
访问数组得到的是数组A
中元素的复制,在循环体中改变A
不会影响a
的值。
goto, break, continue
和C++一致。不过配合goto LABEL
的标签,可以指定break LABEL
或continue LABEL
来指定跳出/重新循环的层级。
函数
1 | func FUNCTION(ARGUMENTS) RETURN_TYPE { |
也支持隐式返回值: 给返回值命名NAME RETURN_TYPE
。然后在程序操作过程中修改NAME
后最后直接return
。
一个简单的多返回值函数例如:
1 | package main |
多变量赋值的时候是现在计算完所有的右值,然后进行赋值操作。
ARGUMENTS
填入...
可以表示任意数量参数。使用range
来访问:
1 | package main |
也可以使用一个匿名函数(闭包):
1 | package main |
匿名函数也可以作为值传递:
1 | package main |
用defer FUNCTION
关键字表示延迟调用,在函数结束的时候(返回返回值以后)再调用FUNCTION
。
1 | package main |
结构体
一个结构体可以嵌入其他结构体,直接获得其他结构体的成员。
1 | type NAME struct { |
结构体的方法:
1 | package main |
这里的(p *Point)
类似于Python的self
。Go语言不支持p->x
这种写法,然而可以直接用指针调用成员函数,即p.x
和(*p).x
等价。
1 | type NEW_TYPE TYPE |
类似于C++的typedef
。
接口
1 | type NAME interface { |
类似于C++中的通用类型(template
)。即对于多个不同的STRUCT
,各自支持一个或几个意义相近而实现不同的函数FUNCTION
,那么就可以用接口来实现通用类型。例如:
1 | package main |
这样AorB
接口既可以作为一个B
的对象又可以作为一个A
的对象。注意要实现一个接口必须要求A
和B
都定义了AorB
中所描述的函数,而且接口函数必须传入值本身而不是指针,本例中必须定义func (a A) F()
而不是func (a *A) F()
。接口对于实现一个通用类型函数非常有用。
异常处理
error
类型是一个接口类型,只需要包含Error()
函数,返回一条错误信息字符串。
可以用errors.New(string)
返回一个error
对象。也可以使用fmt.Errorf(string)
。
panic(SOMETHING)
立即中断当前函数流程,执行延迟调用,在延迟调用中配合recover()
可以将SOMETHING
作为返回值。
error
当没有发生异常时为nil
。
并发
goroutine
相对于普通函数调用F()
,将其写为go F()
就会启动一个新的goroutine
运行目标函数,其比普通的线程更加高效(内存消耗更少,创建与销毁的开销更小,切换开销更小)。
注意函数运行时并不会等待goroutine
,所以往往配合人工制造的等待time.Sleep()
。
channel
A := make(chan TYPE)
来声明一个通道。然后goroutine
之间就可以使用通道来互相传递信息。
A <- SOMETHING
向通道内输入信息。
<-A
将通道内的信息以值的形式输出,使用方法例如SOMETHING = <-A
。输出会导致堵塞: 如果当前通道内没有内容则会一直等待直到有内容输出或通道关闭。
close(A)
来关闭一个通道。
select
语句相当于通道处理的switch
语句,例如:
1 | select { |
这里SOMETHING
往往是一个通道的输入输出动作。如果当前缓冲区内没有滞留的输入输出,就会执行default
,如果没有写default
则会堵塞直到出现一个通道输入输出操作并执行。如果当前缓冲区内已经有多个滞留的输入输出,则会随机执行一个。与无条件for
循环配合可以实现持续监听。
模块
BIF
new()
: 新建一个对象。
len(x)
: 返回一个对象的长度或是包含的元素个数。
append()
: 数组、切片追加元素。
copy(A,B)
: 将B
拷贝到A
,拷贝的实际长度是两者长度的最小值。
imag()
、real()
: 复数的实部和虚部。
delete()
: 删除map
元素。
close()
: 关闭channel
。
panic()
,recover()
: 异常处理。
os
OpenFile(PATH,TYPE,PERM)
: 其中TYPE
包括只读O_RDONLY
,只写O_WRONLY
,可读可写O_RDWR
,创建O_CREATE
。而如果文件不存在,PERM
指定新建文件的权限,一般为0777
表示可读可写。返回一个*os.File
对象和一个error
信息。
一个使用的例子是:
1 | file,_ := os.OpenFile("/a.txt",os.O_WRONLY|os.O_CREATE,0777) |
File.Read([]byte)
读取文件内容,返回读取字节数和error
信息。File.Write()
写入文件。File.Close()
关闭。
getwd()
: 返回当前工作目录绝对路径PATH
和error
信息。
Chdir(PATH)
: 将工作目录改变到PATH
返回error
信息。
strings
Contains(A,B)
: B
是不是A
的子串。
ContainsAny(A,B)
: A
和B
是否有相同字符。
ContainsRune(A,r)
: r
是否是A
的字符。
Index(A,B)
: 返回B
在A
第一次出现的位置或-1。
IndexAny(A,B)
: 返回B
中某个字符在A
第一次出现的位置或-1。
IndexFunc(A,r,F)
: 返回在A
第一次出现的满足F(r)
为true
的字符或-1。
Count(A,B)
: 求B
在A
中出现的次数。
HasPrefix(A,B)
: B
是不是A
的前缀。
HasSuffix(A,B)
: B
是不是A
的后缀。
ToUpper(A)/ToLower(A)
: 转换大小写后返回字符串。
Split(A,TOKEN)
: 将A
按照TOKEN
分割,去掉TOKEN
返回一个字符串数组。
SplitAfter(A,TOKEN)
: 将A
按照TOKEN
分割,TOKEN
分到前一个字符串里返回一个字符串数组。
Join(A[],TOKEN)
: 将A[]
以TOKEN
为分隔符连接返回一个字符串。
Replace(A,B,C,n)
: 将A
中的前n
个子串B
替换为C
返回一个字符串。
strconv
ParseBool(STRING)
: 将STRING
转换为bool
。其中1
,t
,T
,TRUE
,true
,True
都会转换为真,类似的转换为假,其他值得到错误。
ParseFloat(STRING,SIZE)
: 将STRING
转换为float
,SIZE
指定大小(32或64)。
ParseInt(STRING,BASE,SIZE)
: 将STRING
转换为int
,BASE
指定进制,SIZE
指定大小。
Atoi(STRING)
: 将STRING
转换为int
。一般使用这个而不是ParseInt()
。
FormatInt(INT,BASE)
: 将int
转换为STRING
,BASE
指定进制。
Itoa(INT)
: 将int
转换为STRING
。一般使用这个而不是FormatInt()
。
io/ioutil
Reader()
是一个接口。这里的TYPE
只需要自定义Read
函数。同理有Writer()
接口和Closer()
接口。
var a bytes.Buffer
建立一个缓冲区。支持a.Write(SOMETHING)
将数据写入a
,已经a.Read()
读入、a.ReadByte()
逐字节读入和a.ReadRune()
逐4字节读入返回一个字符。
ioutil
包中是许多io
的接口:
Discard
是一个io.Write
对象,其Write()
函数什么也不做。
ReadAll(Reader)
: 读取所有数据([]byte
)。
ReadFile(STRING)
: 读取字符串对应的文件中的所有数据。
WriteFile(STRING,DATA,PERM)
是一个io.Write
对象,清空文件重写,如果文件不存在就创建。DATA
是一个[]byte
。
fmt
Println()
,Print()
,Printf()
,Scanln()
,Scan()
,Scanf()
。
Errorf()
: 输出到标准错误流。
Fprint(writer,...)
,Fprintf(writer,...)
: 输出到writer
,只要是一个io.Writer
对象。
time
time.Sleep(T)
: 睡眠。其中T
是一个时间类型,常用的有time.Second
和time.Millisecond
。例如: time.Sleep(300 * time.Millisecond)
来睡眠300ms。
time.After(T)
: 返回一个通道,睡眠时间T
后这个通道会输出一个消息。
time.Tick(T)
: 返回一个通道,每隔T
时间这个通道就会输出一个消息。
sync
WaitGroup
类型。var a sync.WaitGroup
用于新建一个计数器。每次可以用a.Add(x)
使计数器增加x
,用a.Done()
使计数器减1。a.Wait()
会堵塞直至计数器为0。
Mutex
类型。var a sync.Mutex
用于新建一个锁。a.Lock()
和a.Unlock()
用于加锁和解锁。注意传递锁的时候需要用指针,否则复制新锁,是没有意义的。不支持重复锁定(即一个goroutine
在持有同一把锁的时候再次申请这把锁)。
runtime
Gosched()
: 让当前goroutine
暂停,等待下次调度的时候恢复执行。
Goexit()
: 执行defer
语句后退出当前goroutine
。
NumCPU()
: 当前系统的CPU核数量。
GOMAXPROCS(x)
: 设置最大可同时使用的CPU核数目为(x)。
reflect
注意reflect
速度较慢。
TypeOf(x)
: 获得x
的类型。x.Name()
是这个变量类型(可以是自己定义的类型),x.Kind()
表示这个变量类型的本质(底层实现)。
ValueOf(x)
: 获得x
的值,返回一个reflect.value
对象。value.Interface.(TYPE)
可以强制转换为TYPE
。如果x
是一个指针用value.Elem()
可以得到*x
的值。
value.CanSet(x)
: 表示是否可以更改。
net/http
1 | import "net/http" |
来实现一个服务器。OPERATION
例如:
1 | fmt.Fprintf(w,"Hello World\n") |
扫描二维码即可在手机上查看这篇文章,或者转发二维码来分享这篇文章: