GO语言
go是一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
Go应用领域
Go语言作为服务器编程语言,很适合处理日志、数据打包、虚拟机处理、文件系统、分布式系统、数据库代理等;网络编程方面,Go语言广泛应用于Web应用、API应用、下载应用等;除此之外,Go语言还适用于内存数据库和云平台领域,目前国外很多云平台都是采用Go开发。
- 服务器编程
以前你如果使用C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。
- 微服务
现在越来越多新的项目采用微服务架构,前面介绍的优秀项目中也看到很多Go提供的微服务框架,如git-kit、go-micro等。
举些具体公司的例子。
比如今日头条,2017的一篇文章今日头条使用Go构建了千万级微服务,其中说到有80%的流量都跑在Go上,其中提到头条还开发了自己的一套微服务框架;还有B站也是采用的Go开发。
- 云平台
云服务很多都是采用Go进行开发,比如国内著名的七牛云是全站采用Go开发;还有如盛大CDN、阿里云CDN,华为云等。而且云平台基础设施如docker、kubernetes等也是Go开发;
国内大厂开源的Golang项目
- 字节跳动:kitex,开源
- 滴滴:有自己的一套微服务框架,未开源
- 腾讯:TarsGo,开源,并且是Linux基金会项目
- B站:kratos,开源
- 斗鱼:Jupiter,开源
- 好未来:go-zero,开源
- 阿里:dubbo-go,开源
- 华为:Go Chassis,开源
- 知乎:内部改用go重构后端
定义变量
使用var
关键字是Go最基本的定义变量方式
//定义一个名称为“variableName”,类型为"type"的变量
var variableName type
定义多个变量
//定义三个类型都是“type”的变量
var vname1, vname2, vname3 type
定义变量并初始化值
//初始化“variableName”的变量为“value”值,类型是“type”
var variableName type = value
同时初始化多个变量
var vname1, vname2, vname3 type= v1, v2, v3
忽略类型声明:
var vname1, vname2, vname3 = v1, v2, v3
继续简化:
vname1, vname2, vname3 := v1, v2, v3
:=
这个符号直接取代了var
和type
,这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,所以一般用var
方式来定义全局变量。
_
(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。在这个例子中,我们将值35
赋予b
,并同时丢弃34
:
_, b := 34, 35
Go对于已声明但未使用的变量会在编译阶段报错,比如下面的代码就会产生一个错误:声明了i
但未使用。
package main
func main() {
var i int
}
定义常量
所谓常量,也就是在程序编译阶段就确定下来的值,程序在运行时无法改变该值。在Go程序中,常量可定义为数值、布尔值或字符串等类型。
它的语法如下:
const constantName [type] = value
//可以明确指定常量的类型:
const Pi float32 = 3.1415926
下面是一些常量声明的例子:
package main
import "fmt"
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, "str" //多重赋值
area = LENGTH * WIDTH
fmt.Printf("面积为 : %d", area)
println()
println(a, b, c)
}
数据结构
go语言数据结构有四大类:1、基础类型,包括整型(有符号和无符号整数)、浮点数、复数、字符串(由不可变的字节序列构成)、布尔值(只有true和false两个值);2、聚合类型,包括数组、结构体(是由任意个任意类型的变量组合在一起的数据类型);3、引用类型,包括指针、slice(是一个拥有相同元素的可变长度序列)、map、function、channel;4、接口类型。
整型
有符号整数会分成 int8、int16、int32、int64
无符号整数会分成 uint8、uint16、uint32、uint64
但通常会直接使用 int 和 uint,因为这两种类型会根据平台的特性,自动转成运算效率最高的类型
浮点数
float32 的范围是 1.4e-45 ~ 3.4e38
float64 的范围是 4.9e-324 ~ 1.8e308
复数
complex128(64 位实数和虚数)和 complex64(32 位实数和虚数),其中 complex128 为复数的默认类型。
var name complex128 = complex(x, y)
//上面的声明语句也可以简写为下面的形式
name := complex(x, y)
var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y) // "(-5+10i)"
fmt.Println(real(x*y)) // "-5"
fmt.Println(imag(x*y)) // "10"
布尔
只有两个值: true
和 false
。
字符串
字符串由不可变的字节序列([]byte)构成,字符串的内容通常会用 UTF-8 的编码格式处理。
Go 的字符串内容是不可变的,对当前字符串做的操作都会生成一个新的字符串。Go 字符串天然支持 UTF-8,而且习惯上也会优先使用这种编码方式,乱码的烦恼会少一些。
Go 语言也为字符串提供了丰富的类库:
- bytes:用于操作字节slice([]byte),如果需要对字符串进行频繁的修改, 使用 byte.Buffer 会高效
- strings:用于搜索、替换等等字符传操作
- strconv:主要用于字符串和其他基本数据类型之间的转换
- unicode:用来判断字符的特性,比如是否是数字、是否大写等等
聚合类型的值由内存中的一组变量构成。数组和结构体都是聚合类型,数组和结构体的长度都是固定的。数组中的的元素类型必须都相同,而结构体中的元素可以不同。
数组
数组是一个长度固定,拥有0 个或多个(不超过数组长度)相同数据类型的序列。数组在声明的时候必须指定长度,可以使用常量,但是不能使用变量。
实际上,我们很少在代码中直接使用数组,数组在绝大部分的时候都是做为 slice 的底层存储,并不会直接使用。
数组的可比较性取决于元素的可比较性,如果元素是可比较的,那么数组也是可比较的,反之亦然。
//声明数组
var variable_name [SIZE] variable_type
var balance [10] float32
//初始化数组
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果数组长度不确定,可以使用 ...
代替数组的长度,编译器会根据元素个数自行推断数组的长度:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
//将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
获取数组中的元素
var salary float32 = balance[9]
salary := balance[9]
切片(slice)
切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型
- 切片的声明
var identifier []type
//使用make()函数创建切片
var slice []type = make([]type, len)
//也可以简写为
slice := make([]type, len)
//也可以指定容量,其中 capacity 为可选参数
make([]T, length, capacity)
- 切片初始化
//直接初始化
s :=[] int {1,2,3}
//从数组或切片中生成新的切片
slice [开始位置 : 结束位置]
slice := []int{0,1,2,3,4,5,6,7,8}
//新切片从索引1(包含) 到索引4(不包含)
newSlice := slice[1:4] //[1 2 3]
指针
即内存地址
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。
Go语言中使用在变量名前面添加&
操作符(前缀)来获取变量的内存地址(取地址操作),格式如下:
//获取a的指针(内存地址)
var a int = 10
ptr := &v
//直接声明指针
var ptr *var-type
var ip *int
在指针类型前面加上 * 号(前缀)来获取指针指向的地址中的内容
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
字典(map)
//使用 make 函数声明map
map_name := make(map[KeyType]ValueType, capacity)
// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
// 直接初始化创建Map
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
// 获取 Map 的长度
len := len(m)
// 删键值对
delete(m, "banana")
函数
//函数的声明
func function_name(参数列表) [返回参数列表] {
函数体
}
//声明一个函数
func test(id int, name string) (string, string) {
//处理逻辑
return "a", "b"
}
- 匿名函数
func(参数列表) [返回参数列表]{
函数体
}
//匿名函数可以在声明后调用:
func(data int){
fmt.Println("Hello", data)
}(100)
//匿名函数体可以被赋值
// 将匿名函数体保存到 f() 中
f := func(data int) {
fmt.Println("Hello", data)
}
// 使用 f() 调用
f(100)
- 匿名函数作为回调函数
package main
import "fmt"
// 遍历切片的每个元素,通过给定函数进行元素访问
func visit(list []int, f func(int)) {
for _, v := range list {
f(v)
}
}
func main() {
//使用匿名函数打印切片内容
visit([]int{1, 2, 3, 4}, func(v int) {
fmt.Println(v)
})
}
- 递归函数
func recursion() {
recursion() /* 函数调用自身 */
}
结构体
结构体是由任意个任意类型的变量组合在一起的数据类型,和其他语言中类的概念相似。
type Person struct {
Name string
age int
}
go不是面向对象语言,面向对象是一种思想,go语言可以通过结构体来实现面向对象
结构体匿名字段
package main
import "fmt"
type Person struct {
Name string
age int
}
type Student struct {
id int
Person //匿名
int //匿名
}
func main() {
p := Person{"enzo", 10}
student1 := Student{p, 111}
fmt.Println(student1, student1.Name, student1.age)
student2 := Student{Person{"hx", 10}, 111}
fmt.Println(student2)
}
函数方法
即相应结构体可调用的方法
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
实例:
package main
import "fmt"
type Person struct {
Name string
age int
}
func (p Person) say() string {
return "my name is " + p.Name + " " + strconv.Itoa(p.age) + " years old"
}
func main() {
p := Person{"enzo", 10}
fmt.Println(p.say())
}
接口
interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。
Go 语言的接口设计是非侵入式的,接口编写者无须知道接口被哪些类型实现。而接口实现者只需知道实现的是什么样子的接口,但无须指明实现哪一个接口。编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。
非侵入式设计是 Go 语言设计师经过多年的大项目经验总结出来的设计之道。只有让接口和实现者真正解耦,编译速度才能真正提高,项目之间的耦合度也会降低不少。
type 接口名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
// 定义interface
type Human interface {
Say()
Eat(food string)
}
接口实现
package main
import (
"fmt"
"strconv"
)
type Person struct {
Name string //首字母大写,对外可见
age int //首字母小写,对外不可见
}
type Student struct {
Person
id int
}
func (p Person) Say() {
fmt.Println("my name is " + p.Name + " " + strconv.Itoa(p.age) + " years old")
}
func (p Person) Eat() {
fmt.Println("i like eat")
}
func (s Student) Learn() {
fmt.Println("i like Learning")
}
type Human interface {
Say()
Eat()
}
type Learner interface {
Learn()
}
func main() {
p := Person{"enzo", 10}
p.Say()
p.Eat()
student := Student{Person{"hx", 20}, 111}
student.Say()
student.Eat()
student.Learn()
var h Human = p
h.Say()
var l Learner = student
l.Learn()
}
- 空interface
空接口是接口类型的特殊形式,空接口没有任何方法,因此任何类型都无须实现空接口。从实现的角度看,任何值都满足这个接口的需求。因此空接口类型可以保存任何值,也可以从空接口中取出原值。
// 定义a为空接口
var a interface{}
fmt.Println(a) //<nil>
var num int = 5
str := "Hello world"
// a可以存储任意类型的数值
a = num
fmt.Println(a) //5
a = str
fmt.Println(a) //Hello world
- 类型断言
value.(type)
即判断value是否是指定的type类型
可以对接口中的值进行类型断言,来获取接口中的值和判断值类型
package main
func main() {
var i interface{} = "Hello, World"
//var i interface{} = 1
str, ok := i.(string) //返回两个值,ok为true或者false,如果false,值为空
if ok {
println(str)
} else {
println("i 不是string类型")
}
}
通道(channel)
通道是用来传递数据的一个数据结构,可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。
必须使用make 创建channel:
ch := make(chan int)
ch := make(chan string)
ch := make(chan interface{})
操作符 <-
用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
通道有双通道和单通道之分,如果未指定方向,则为双向通道。
package main
import (
"fmt"
"time"
)
// 单通道,将数据发送到goroutine
func produce(p chan<- int) {
for i := 0; i < 10; i++ {
p <- i
//i := <-p //错误示例,只能发送数据到通道中,不能从通道中获取数据
}
}
// 单通道,接收goroutine中的数据
func consumer(c <-chan int) {
for i := 0; i < 10; i++ {
v := <-c
fmt.Println("receive:", v)
//c <- i //错误示例,只能从通道中接收数据,不能发送数据到通道中
}
}
// 双通道,可发送和接收数据
func user(p chan int) {
//将数据发送到goroutine
for i := 0; i < 10; i++ {
p <- i
}
//接收goroutine中的数据
for i := 0; i < 10; i++ {
a := <-p
fmt.Println(a)
}
}
func main() {
ch := make(chan int, 10)
go produce(ch)
go consumer(ch)
time.Sleep(100 * time.Millisecond)
go user(ch)
time.Sleep(100 * time.Millisecond)
}
并发(goroutine)
goroutine是Go并发设计的核心。goroutine说到底其实就是协程,是比线程更小的一种执行单元,执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩(几十到几百K)。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过go
关键字实现了,其实就是一个普通的函数。
go say()
通过关键字go就启动了一个goroutine。我们来看一个例子
package main
import (
"fmt"
"runtime"
)
func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
go say("world") //开一个新的goroutine执行
say("hello") //当前Goroutines执行
}
// 以上程序执行后将输出:
// hello
// world
// hello
// world
// hello
// world
// hello
// world
// hello
流程控制
if
满足条件就做某事,否则做另一件事。
Go里面if
条件判断语句中不需要括号,如下代码所示
if x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than 10")
}
Go的if
还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示
// 计算获取值x,然后根据x返回的大小,判断是否大于10。
if x := computedValue(); x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than 10")
}
//这个地方如果这样调用就编译出错了,因为x是条件里面的变量
fmt.Println(x)
多个条件的时候如下所示:
if integer == 3 {
fmt.Println("The integer is equal to 3")
} else if integer < 3 {
fmt.Println("The integer is less than 3")
} else {
fmt.Println("The integer is greater than 3")
}
for
Go里面最强大的一个控制逻辑就是for
,它既可以用来循环读取数据,又可以当作while
来控制逻辑,还能迭代操作。它的语法如下:
for init; condition; post {
//...
}
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
例子:
package main
import "fmt"
func main(){
sum := 0;
for index:=0; index < 10 ; index++ {
sum += index
}
fmt.Println("sum is equal to ", sum)
}
// 输出:sum is equal to 45
有些时候需要进行多个赋值操作,由于Go里面没有,
操作符,那么可以使用平行赋值i, j = i+1, j-1
有些时候如果我们忽略init
和post
:
sum := 1
for ; sum < 1000; {
sum += sum
}
其中;
也可以省略,那么就变成如下的代码了,类似while
sum := 1
for sum < 1000 {
sum += sum
}
在循环里面有两个关键操作break
和continue
,break
操作是跳出当前循环,continue
是跳过本次循环。当嵌套过深的时候,break
可以配合标签使用,即跳转至标签所指定的位置,详细参考如下例子:
for index := 10; index>0; index-- {
if index == 5{
break // 或者continue
}
fmt.Println(index)
}
// break打印出来10、9、8、7、6
// continue打印出来10、9、8、7、6、4、3、2、1
break
和continue
还可以跟着标号,用来跳到多重循环中的外层循环
for
配合range
可以用于读取slice
和map
的数据:
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}
由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用_
来丢弃不需要的返回值 例如
for _, v := range map{
fmt.Println("map's val:", v)
}
label
break label,跳出label代码块
label:
for i := 0; i < 10; i++ {
fmt.Println(i)
if i == 5 {
break label
}
}
//打印0~5
continue label,跳出当前循环,继续label代码块
label:
for i := 0; i < 10; i++ {
fmt.Println(i)
if i == 5 {
continue label
}
}
//打印0~9
switch
有些时候你需要写很多的if-else
来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候switch
就能很好的解决这个问题。它的语法如下
switch sExpr {
case expr1:
some instructions
case expr2:
some other instructions
case expr3:
some other instructions
default:
other code
}
sExpr
和expr1
、expr2
、expr3
的类型必须一致。Go的switch
非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果switch
没有表达式,它会匹配true
。
i := 10
switch i {
case 1:
fmt.Println("i is equal to 1")
case 2, 3, 4:
fmt.Println("i is equal to 2, 3 or 4")
case 10:
fmt.Println("i is equal to 10")
default:
fmt.Println("All I know is that i is an integer")
}
在第5行中,我们把很多值聚合在了一个case
里面,同时,Go里面switch
默认相当于每个case
最后带有break
,匹配成功后不会自动向下执行其他case,而是跳出整个switch
, 但是可以使用fallthrough
强制执行后面的case代码。
integer := 6
switch integer {
case 4:
fmt.Println("The integer was <= 4")
fallthrough
case 5:
fmt.Println("The integer was <= 5")
fallthrough
case 6:
fmt.Println("The integer was <= 6")
fallthrough
case 7:
fmt.Println("The integer was <= 7")
fallthrough
case 8:
fmt.Println("The integer was <= 8")
fallthrough
default:
fmt.Println("default case")
}
上面的程序将输出
The integer was <= 6
The integer was <= 7
The integer was <= 8
default case
select
select 语句类似于 switch语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
select 语句只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收。
select 语句会监听所有指定的通道上的操作,一旦其中一个通道准备好就会执行相应的代码块。
如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。
package main
import (
"fmt"
)
func main() {
c1 := make(chan string, 3)
c2 := make(chan string, 3)
for i := 0; i < 3; i++ {
go func() {
c1 <- "one"
}()
go func() {
c2 <- "two"
}()
}
for i := 0; i < 10; i++ {
select {
case message1 := <-c1:
fmt.Println(message1)
case message2 := <-c2:
fmt.Println(message2)
default:
fmt.Println("no message")
}
}
}
//one
//one
//two
//one
//two
//two
//no message
//no message
//no message
//no message
goto
Go有goto
语句——请明智地使用它。用goto
跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
func myFunc() {
i := 0
Here: //这行的第一个词,以冒号结束作为标签
println(i)
i++
goto Here //跳转到Here去
}
标签名是大小写敏感的。
字符串操作
下面这些函数来自于strings包,这里介绍一些我平常经常用到的函数,更详细的请参考官方的文档。
-
func Contains(s, substr string) bool
字符串s中是否包含substr,返回bool值
fmt.Println(strings.Contains("seafood", "foo"))
fmt.Println(strings.Contains("seafood", "bar"))
fmt.Println(strings.Contains("seafood", ""))
fmt.Println(strings.Contains("", ""))
//Output:
//true
//false
//true
//true
-
func Join(a []string, sep string) string
字符串链接,把slice a通过sep链接起来
s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))
//Output:foo, bar, baz
-
func Index(s, sep string) int
在字符串s中查找sep所在的位置,返回位置值,找不到返回-1
fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
//Output:4
//-1
-
func Repeat(s string, count int) string
重复s字符串count次,最后返回重复的字符串
fmt.Println("ba" + strings.Repeat("na", 2))
//Output:banana
-
func Replace(s, old, new string, n int) string
在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换
fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
//Output:oinky oinky oink
//moo moo moo
-
func Split(s, sep string) []string
把s字符串按照sep分割,返回slice
fmt.Printf("%q\n", strings.Split("a,b,c", ","))
fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
fmt.Printf("%q\n", strings.Split(" xyz ", ""))
fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
//Output:["a" "b" "c"]
//["" "man " "plan " "canal panama"]
//[" " "x" "y" "z" " "]
//[""]
-
func Trim(s string, cutset string) string
在s字符串的头部和尾部去除cutset指定的字符串
fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! "))
//Output:["Achtung"]
-
func Fields(s string) []string
去除s字符串的空格符,并且按照空格分割返回slice
fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
//Output:Fields are: ["foo" "bar" "baz"]
字符串转换
字符串转化的函数在strconv中,如下也只是列出一些常用的:
- Append 系列函数将整数等转换为字符串后,添加到现有的字节数组中。
package main
import (
"fmt"
"strconv"
)
func main() {
str := make([]byte, 0, 100)
str = strconv.AppendInt(str, 4567, 10)
str = strconv.AppendBool(str, false)
str = strconv.AppendQuote(str, "abcdefg")
str = strconv.AppendQuoteRune(str, '单')
fmt.Println(string(str))
}
- Format 系列函数把其他类型的转换为字符串
package main
import (
"fmt"
"strconv"
)
func main() {
a := strconv.FormatBool(false)
b := strconv.FormatFloat(123.23, 'g', 12, 64)
c := strconv.FormatInt(1234, 10)
d := strconv.FormatUint(12345, 10)
e := strconv.Itoa(1023)
fmt.Println(a, b, c, d, e)
}
- Parse 系列函数把字符串转换为其他类型
package main
import (
"fmt"
"strconv"
)
func checkError(e error){
if e != nil{
fmt.Println(e)
}
}
func main() {
a, err := strconv.ParseBool("false")
checkError(err)
b, err := strconv.ParseFloat("123.23", 64)
checkError(err)
c, err := strconv.ParseInt("1234", 10, 64)
checkError(err)
d, err := strconv.ParseUint("12345", 10, 64)
checkError(err)
e, err := strconv.Atoi("1023")
checkError(err)
fmt.Println(a, b, c, d, e)
}
文件操作
目录操作
文件操作的大多数函数都是在os包里面,下面列举了几个目录操作的:
func Mkdir(name string, perm FileMode) error
创建名称为name的目录,权限设置是perm,例如0777
func MkdirAll(path string, perm FileMode) error
根据path创建多级子目录,例如enzo/test1/test2。
func Remove(name string) error
删除名称为name的目录,当目录下有文件或者其他目录时会出错
func RemoveAll(path string) error
根据path删除多级子目录,如果path是单个名称,那么该目录下的子目录全部删除。
package main
import "os"
func main() {
//os.Mkdir("C:/Users/Enzo/Desktop/enzo", 0777)
os.MkdirAll("C:/Users/Enzo/Desktop/enzo/test1/test2", 0777)
//err := os.Remove("C:/Users/Enzo/Desktop/enzo")
//if err != nil {
// fmt.Println(err)
//}
//os.RemoveAll("C:/Users/Enzo/Desktop/enzo")
}
建文件
新建文件可以通过如下两个方法
func Create(name string) (file *File, err Error)
根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的。
func NewFile(fd uintptr, name string) *File
根据文件描述符创建相应的文件,返回一个文件对象
打开文件
func Open(name string) (file *File, err Error)
该方法打开一个名称为name的文件,但是是只读方式,内部实现其实调用了OpenFile。
func OpenFile(name string, flag int, perm uint32) (file *File, err Error)
打开名称为name的文件,flag是打开的方式,只读、读写等,perm是权限
写文件
写文件函数:
func (file *File) Write(b []byte) (n int, err Error)
写入byte类型的信息到文件
func (file *File) WriteAt(b []byte, off int64) (n int, err Error)
在指定位置开始写入byte类型的信息
func (file *File) WriteString(s string) (ret int, err Error)
写入string信息到文件
package main
import (
"fmt"
"os"
)
func main() {
os.Mkdir("C:/Users/Enzo/Desktop/enzo", 0777)
userFile := "C:/Users/Enzo/Desktop/enzo/enzo.txt"
fout, err := os.Create(userFile)
if err != nil {
fmt.Println(userFile, err)
return
}
defer fout.Close()
for i := 0; i < 10; i++ {
fout.WriteString("Just a test!\r\n")
fout.Write([]byte("Just a test!\r\n"))
}
}
读文件
读文件函数:
func (file *File) Read(b []byte) (n int, err Error)
读取数据到b中
func (file *File) ReadAt(b []byte, off int64) (n int, err Error)
从off开始读取数据到b中
package main
import (
"fmt"
"os"
)
func main() {
userFile := "asatxie.txt"
fl, err := os.Open(userFile)
if err != nil {
fmt.Println(userFile, err)
return
}
defer fl.Close()
buf := make([]byte, 1024)
for {
n, _ := fl.Read(buf)
if 0 == n {
break
}
os.Stdout.Write(buf[:n])
}
}
删除文件
Go语言里面删除文件和删除文件夹是同一个函数
func Remove(name string) Error
调用该函数就可以删除文件名为name的文件