目录
结构体:type Employee struct { }
函 数:func functionname(parametername type) returntype { }
方 法:func (t Type) methodName(parameter list) { }
接 口:type VowelsFinder interface { FindVowels() []rune }
1 regexp内置包
正则表达式(regular expression)就是由元字符组成的一种字符串匹配的模式,使用这种模式可以实现对文本内容解析、校验、替换。
1)检查正则表达式与字节数组是否匹配。
func Match(pattern string,b []byte)(method bool,err error)
flag,er = regexp.Match("[1-9]",[]byte("123456"))
2)检查正则表达式与字符串是否匹配。
func MatchString(pattern string,s string)(method bool,err error)
flag,_ := regexp.MatchString("[1-9]","123456")
3)将正则表达式字符串编译成正则表达式对象(Regexp)。
func Compile(expr string) (*regexp,error)
Myregexp,_ := regexp.Compile("[1-9]")
4)MustCompile()用法同Compile(),但是不返回error。如果表达式不能被解析就会panic。
func MustCompile(str string) *regexp
Myregexp:= regexp.MustCompile("[1-9]")
5)判断Regexp正则对象是否与给定的字节数组匹配。
func (re *Request) Match(b []byte) bool
Myregexp:= regexp.MustCompile("[1-9]")
flag := Myregexp.Match([]byte("didi"))
6)判断Regexp正则对象是否与给定的字符串匹配。
func (re *Request) MatchString(s string) bool
Myregexp:= regexp.Compile("[1-9]")
flag := Myregexp.MatchString("didi"))
7)ReplaceAll()将src中符合正则表达式的部分全部替换成指定内容。
func (re *regexp) ReplaceAll(src,repl []byte) []byte
Myregexp:= regexp.MustCompile("[1-9]")
result := string(Myregexp.ReplaceAll([]byte("dddd"),[]byte("12"))))
8)将字符串按照正则表达式分割成子字符串组成的切片。
func (re *regexp) Split(s string,n int) []string
Myregexp:= regexp.MustCompile("[1-9]")
result := Myregexp.Split("dddd",5)
2 指针
2.1 声明指针
指针变量的类型为 *T,该指针指向一个 T 类型的变量。指针的零值是 nil。
指针的解引用可以获取指针所指向的变量的值,解引用的语法是 *a。
func main() {
b := 255
var a *int = &b //& 操作符用于获取变量的地址。
fmt.Printf("Type of a is %T\n", a)
fmt.Println("address of b is", a)
fmt.Println("value of b is", *a)
}
2.2 函数传递指针
func change(val *int) {
*val = 55
}
func main() {
a := 58
fmt.Println("value of a before function call is",a)
b := &a
change(b)
fmt.Println("value of a after function call is", a)
}
不要向函数传递数组的指针,而应该使用切片
func modify(arr *[3]int) {
(*arr)[0] = 90 //a[x] 是 (*a)[x] 的简写形式,(*arr)[0] 可以替换为 arr[0]。
}
func main() {
a := [3]int{89, 90, 91}
modify(&a)
fmt.Println(a)
}
3 结构体
3.1 结构体声明
1)创建名的结构体(Named Structure)。创建了名为 Employee 的新类型,而它可以用于创建 Employee 类型的结构体变量。
type Employee struct {
firstName string
lastName string
age,salary int
}
func main() {
//creating structure using field names
emp1 := Employee{
firstName: "Sam",
age: 25,
salary: 500,
lastName: "Anderson",
}
//creating structure without using field names
emp2 := Employee{"Thomas", "Paul", 29, 800}
fmt.Println("Employee 1", emp1)
fmt.Println("Employee 2", emp2)
}
2)声明结构体时也可以不用声明一个新类型,这样的结构体类型称为 匿名结构体(Anonymous Structure)。
var employee struct {
firstName, lastName string
age,salary int
}
func main() {
emp3 := struct {
firstName, lastName string
age, salary int
}{
firstName: "Andreah",
lastName: "Nikola",
age: 31,
salary: 5000,
}
fmt.Println("Employee 3", emp3)
}
3)访问字段
fmt.Println("First Name:", emp3.firstName)
fmt.Println("Last Name:", emp3.lastName)
fmt.Println("Age:", emp3.age)
fmt.Printf("Salary: $%d", emp3.salary)
3.2 结构体指针
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
emp8 := &Employee{"Sam", "Anderson", 55, 6000}
fmt.Println("First Name:", (*emp8).firstName)
fmt.Println("Age:", (*emp8).age)
}
3.3 匿名字段
当我们创建结构体时,字段可以只有类型,而没有字段名。这样的字段称为匿名字段(Anonymous Field)。
type Person struct {
string
int
}
func main() {
p := Person{"Naveen", 50}
fmt.Println(p)
p.string = "King"
p.int = 100
fmt.Println(p)
}
3.4 结构体替代类
Go 不支持类,而是提供了结构体。结构体中可以添加方法。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。(方法将在第4节介绍)
1)在你的 Go 工作区创建一个名为 oop 的文件夹。在 opp 中再创建子文件夹 employee。在 employee 内,创建一个名为 employee.go 的文件。
文件夹结构会是这样:
workspacepath -> oop -> employee -> employee.go
package employee
type Employee struct {
FirstName string
LastName string
TotalLeaves int
LeavesTaken int
}
func (e Employee) LeavesRemaining() {
fmt.Printf("%s %s has %d leaves remaining", e.FirstName, e.LastName, (e.TotalLeaves - e.LeavesTaken))
}
在上述程序里,首先指定了该文件属于 employee 包。然后声明了一个 Employee 结构体。其次,结构体 Employee 添加了一个名为 LeavesRemaining 的方法。
2)接着在 oop 文件夹里创建一个文件,命名为 main.go。
现在目录结构如下所示:
workspacepath -> oop -> employee -> employee.go
workspacepath -> oop -> main.go
package main
import "oop/employee"
func main() {
e := employee.Employee {
FirstName: "Sam",
LastName: "Adolf",
TotalLeaves: 30,
LeavesTaken: 20,
}
e.LeavesRemaining()
}
3)使用New函数
package employee
import (
"fmt"
)
type employee struct {
firstName string
lastName string
totalLeaves int
leavesTaken int
}
func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee {
e := employee {firstName, lastName, totalLeave, leavesTaken}
return e
}
func (e employee) LeavesRemaining() {
fmt.Printf("%s %s has %d leaves remaining", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}
我们把 Employee 结构体的首字母改为小写 e,也就是将 type Employee struct 改为了 type employee struct。通过这种方法,我们把 employee 结构体变为了不可引用的,防止其他包对它的访问。
package main
import "oop/employee"
func main() {
e := employee.New("Sam", "Adolf", 30, 20)
e.LeavesRemaining()
}
3.5 组合取代继承
在 Go 中,通过在结构体内嵌套结构体,可以实现组合。
type author struct {
firstName string
lastName string
bio string
}
func (a author) fullName() string {
return fmt.Sprintf("%s %s", a.firstName, a.lastName)
}
type post struct {
title string
content string
author //嵌套author结构体
}
func (p post) details() {
fmt.Println("Title: ", p.title)
fmt.Println("Content: ", p.content)
fmt.Println("Author: ", p.fullName())
fmt.Println("Bio: ", p.bio)
}
type website struct {
posts []post //嵌套post结构体的切片
}
func (w website) contents() {
fmt.Println("Contents of Website\n")
for _, v := range w.posts {
v.details()
fmt.Println()
}
}
4 方法
方法其实就是一个函数,在 func 这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的。
func (t Type) methodName(parameter list) {
}
type Employee struct {
name string
salary int
currency string
}
/*
displaySalary() 方法将 Employee 做为接收器类型
*/
func (e Employee) displaySalary() {
fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
/*
displaySalary2()方法被转化为一个函数,把 Employee 当做参数传入。
*/
func displaySalary2(e Employee) {
fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
func main() {
emp1 := Employee {
name: "Sam Adolf",
salary: 5000,
currency: "$",
}
emp1.displaySalary() // 调用 Employee 类型的 displaySalary() 方法
displaySalary2(emp1)
}
5 接口
5.1 声明接口
在 Go 语言中,接口就是方法签名(Method Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。
//interface definition
type VowelsFinder interface {
FindVowels() []rune
}
type MyString string
//MyString implements VowelsFinder
func (ms MyString) FindVowels() []rune {
。。。
return vowels
}
func main() {
name := MyString("Sam Anderson")
var v VowelsFinder
v = name // possible since MyString implements VowelsFinder
fmt.Printf("Vowels are %c", v.FindVowels())
}
5.2 空接口
没有包含方法的接口称为空接口。空接口表示为 interface{}。由于空接口没有方法,因此所有类型都实现了空接口。
func describe(i interface{}) {
fmt.Printf("Type = %T, value = %v\n", i, i)
}
func main() {
s := "Hello World"
describe(s)
i := 55
describe(i)
}
5.3 类型断言与类型选择
类型断言用于提取接口的底层值(Underlying Value)。
在语法 i.(T) 中,接口 i 的具体类型是 T,该语法用于获得接口的底层值。
func assert(i interface{}) {
s := i.(int) //get the underlying int value from i
fmt.Println(s)
}
func main() {
var s interface{} = 56
assert(s)
类型选择的语法类似于类型断言。类型断言的语法是 i.(T),而对于类型选择,类型 T 由关键字 type 代替。
func findType(i interface{}) {
switch i.(type) {
case string:
fmt.Printf("I am a string and my value is %s\n", i.(string))
case int:
fmt.Printf("I am an int and my value is %d\n", i.(int))
default:
fmt.Printf("Unknown type\n")
}
}
5.4 指针接收实现接口
type Describer interface {
Describe()
}
type Person struct {
name string
age int
}
func (p Person) Describe() { // 使用值接收者实现
fmt.Printf("%s is %d years old\n", p.name, p.age)
}
type Address struct {
state string
country string
}
func (a *Address) Describe() { // 使用指针接收者实现
fmt.Printf("State %s Country %s", a.state, a.country)
}
func main() {
var d1 Describer
p1 := Person{"Sam", 25}
d1 = p1
d1.Describe()
//指针方式
p2 := Person{"James", 32}
d1 = &p2
d1.Describe()
}
5.5 接口嵌套
尽管 Go 语言没有提供继承机制,但可以通过嵌套其他的接口,创建一个新接口。
type SalaryCalculator interface {
DisplaySalary()
}
type LeaveCalculator interface {
CalculateLeavesLeft() int
}
type EmployeeOperations interface {
SalaryCalculator
LeaveCalculator
}
type Employee struct {
firstName string
lastName string
basicPay int
pf int
totalLeaves int
leavesTaken int
}
func (e Employee) DisplaySalary() {
fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}
func (e Employee) CalculateLeavesLeft() int {
return e.totalLeaves - e.leavesTaken
}
func main() {
e := Employee {
firstName: "Naveen",
lastName: "Ramanathan",
basicPay: 5000,
pf: 200,
totalLeaves: 30,
leavesTaken: 5,
}
var empOp EmployeeOperations = e //使用嵌套接口
empOp.DisplaySalary()
fmt.Println("\nLeaves left =", empOp.CalculateLeavesLeft())
}
5.6 多态
所有实现了接口的类型,都可以把它的值保存在一个接口类型的变量中。在 Go 中,我们使用接口的这种特性来实现多态。
type Income interface {
calculate() int
source() string
}
type FixedBilling struct {
projectName string
biddedAmount int
}
type TimeAndMaterial struct {
projectName string
noOfHours int
hourlyRate int
}
func (fb FixedBilling) calculate() int {
return fb.biddedAmount
}
func (fb FixedBilling) source() string {
return fb.projectName
}
func (tm TimeAndMaterial) calculate() int {
return tm.noOfHours * tm.hourlyRate
}
func (tm TimeAndMaterial) source() string {
return tm.projectName
}
func calculateNetIncome(ic []Income) {
var netincome int = 0
for _, income := range ic {
fmt.Printf("Income From %s = $%d\n", income.source(), income.calculate())
netincome += income.calculate()
}
fmt.Printf("Net income of organisation = $%d", netincome)
}
func main() {
project1 := FixedBilling{projectName: "Project 1", biddedAmount: 5000}
project2 := FixedBilling{projectName: "Project 2", biddedAmount: 10000}
project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 25}
incomeStreams := []Income{project1, project2, project3}
calculateNetIncome(incomeStreams)
}
calculateNetIncome函数接收一个 Income 接口类型的切片作为参数。该函数会遍历这个接口切片,并依个调用 calculate() 方法,计算出总收入。该函数同样也会通过调用 source() 显示收入来源。根据 Income 接口的具体类型,程序会调用不同的 calculate() 和 source() 方法。于是,我们在 calculateNetIncome 函数中就实现了多态。
6 并发
并发是指立即处理多个任务的能力。并行是指同时处理多个任务。
》》》假如在他晨跑时,鞋带突然松了。于是他停下来,系一下鞋带,接下来继续跑。这个例子就是典型的并发。这个人能够一下搞定跑步和系鞋带两件事,即立即处理多个任务。
》》》假如这个人在慢跑时,还在用他的 iPod 听着音乐。在这里,他是在跑步的同时听音乐,也就是同时处理多个任务。这称之为并行。
Go 编程语言原生支持并发。Go 使用 Go 协程(Goroutine) 和信道(Channel)来处理并发。
6.1 协程
Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。因此在 Go 应用中,常常会看到有数以千计的 Go 协程并发地运行。
如何创建协程 :
调用函数或者方法时,在前面加上关键字 go,可以让一个新的 Go 协程并发地运行。
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
会发现主函数并不会等待go协程, 因为hello() 函数与 main() 函数会并发地执行。主函数会运行在一个特有的 Go 协程上,它称为 Go 主协程(Main Goroutine)。
可以加上时间限制,让主协程等待一下
func main() {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}
6.2 信道
信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收。
1)信道的声明
所有信道都关联了一个类型。信道只能运输这种类型的数据,而运输其他类型的数据都是非法的。
chan T 表示 T 类型的信道。应用 make 来定义信道,信道的零值为 nil。
func main() {
var a chan int
if a == nil {
fmt.Println("channel a is nil, going to define it")
a = make(chan int)
fmt.Printf("Type of a is %T", a)
}
}
2)数据传送
data := <- a // 读取信道 a
a <- data // 写入信道 a
发送与接收默认是阻塞的。当把数据发送到信道时,程序控制 会在发送数据的语句处 发生阻塞,直到有其它 Go 协程从信道读取到数据,才会解除阻塞。
信道的这种特性能够帮助 Go 协程之间进行高效的通信,不需要用到其他编程语言常见的显式锁或条件变量。
func hello(done chan bool) {
fmt.Println("Start write data")
done <- true
}
func main() {
done := make(chan bool)
go hello(done)
<-done
fmt.Println("read data ")
}
3)死锁
使用信道需要考虑的一个重点是死锁。当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。
同理,当有 Go 协程等着从一个信道接收数据时,我们期望其他的 Go 协程会向该信道写入数据,要不然程序就会触发 panic。
4)单向通信
chan<- T 以创建单向信道,这种信道只能接收数据。也可以创建只发送数据的单向信道。
func sendData(sendch chan<- int) {
sendch <- 10
}
func main() {
sendch := make(chan<- int)
go sendData(sendch)
fmt.Println(<-sendch)
}
5)关闭信道
数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来。
当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。
v, ok := <- ch
func producer(chnl chan int) {
for i := 0; i < 10; i++ {
chnl <- i
}
close(chnl)
}
func main() {
ch := make(chan int)
go producer(ch)
for {
v, ok := <-ch
if ok == false {
break
}
fmt.Println("Received ", v, ok)
}
}
6.3 缓冲信道
1)创建缓冲信道
ch := make(chan type, capacity)
capacity 应该大于 0。无缓冲信道的容量默认为 0。
func main() {
ch := make(chan string, 2)
ch <- "naveen"
ch <- "paul"
fmt.Println(<- ch)
fmt.Println(<- ch)
}
我们创建了一个缓冲信道,其容量为 2。由于该信道的容量为 2,因此可向它写入两个字符串,而且不会发生阻塞。
2)容量与长度
缓冲信道的容量是指信道可以存储的值的数量。我们在使用 make 函数创建缓冲信道的时候会指定容量大小。
缓冲信道的长度是指信道中当前排队的元素个数。
func main() {
ch := make(chan string, 3)
ch <- "naveen"
ch <- "paul"
fmt.Println("capacity is", cap(ch))
fmt.Println("length is", len(ch))
fmt.Println("read value", <-ch)
fmt.Println("new length is", len(ch))
}
6.4 WaitGroup
1)WaitGroup
WaitGroup 用于等待一批 Go 协程执行结束。程序控制会一直阻塞,直到这些协程全部执行完毕。
func process(i int, wg *sync.WaitGroup) {
fmt.Println("started Goroutine ", i)
time.Sleep(2 * time.Second)
fmt.Printf("Goroutine %d ended\n", i)
wg.Done()
}
func main() {
no := 3
var wg sync.WaitGroup
for i := 0; i < no; i++ {
wg.Add(1)
go process(i, &wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
}
WaitGroup 是一个结构体类型,我们创建了 WaitGroup 类型的变量,其初始值为零值。WaitGroup 使用计数器来工作。当我们调用 WaitGroup 的 Add 并传递一个 int 时,WaitGroup 的计数器会加上 Add 的传参。要减少计数器,可以调用 WaitGroup 的 Done() 方法。Wait() 方法会阻塞调用它的 Go 协程,直到计数器变为 0 后才会停止阻塞。
6.5 select
select 语句用于在多个发送/接收信道操作中进行选择。select 语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select 会随机地选取其中之一执行。该语法与 switch 类似,所不同的是,这里的每个 case 语句都是信道操作。
func server1(ch chan string) {
time.Sleep(6 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
6.6 Mutex
1)临界区
当程序并发地运行时,多个 Go 协程不应该同时访问那些修改共享资源的代码。这些修改共享资源的代码称为临界区。
2)Mutex
Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件。
Mutex 可以在 sync 包内找到。Mutex 定义了两个方法:Lock 和 Unlock。所有在 Lock 和 Unlock 之间的代码,都只能由一个 Go 协程执行,于是就可以避免竞态条件。
import (
"fmt"
"sync"
)
var x = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
m.Lock()
x = x + 1
m.Unlock()
wg.Done()
}
func main() {
var w sync.WaitGroup
var m sync.Mutex
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w, &m)
}
w.Wait()
fmt.Println("final value of x", x)
}
7 异常处理
7.1 error接口
error是一个接口类型,包含一个Error()方法。
type error interface
{
Error() string
}
在Go语言中处理错误的方式通常是将返回的错误与nil进行比较。nil值表示没有发生错误,而非nil值表示出现错误。
import (
"errors"
"fmt"
)
func main(){
res , err := Sqrt(-100)
if err != nil {
fmt.Println(err)
//fmt.Println(err.Error())
} else {
fmt.Println(res)
}
}
errors包下的New()函数返回error对象,errors.New()函数创建新的错误。
//1、创建error对象的方式1
err1 := errors.New("自己创建的错误!")
//2、创建error对象的方式2
err2 := fmt.Errorf("错误的类型%d", 10)
7.2 panic和recover
1)panic
panic()是一个内建函数,可以中断原有的控制流程。
func panic(interface{})
func fullName(firstName *string, lastName *string) {
if firstName == nil {
panic("runtime error: first name cannot be nil")
}
if lastName == nil {
panic("runtime error: last name cannot be nil")
}
fmt.Printf("%s %s\n", *firstName, *lastName)
fmt.Println("returned normally from fullName")
}
func main() {
firstName := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}
当函数发生 panic 时,它会终止运行,在执行完所有的延迟函数后,程序控制返回到该函数的调用方。这样的过程会一直持续下去,直到当前协程的所有函数都返回退出,然后程序会打印出 panic 信息,接着打印出堆栈跟踪,最后程序终止。
2)recover
recover 是一个内建函数,用于重新获得 panic 协程的控制。
func recover() interface{}
func recoverName() {
if r := recover(); r!= nil {
fmt.Println("recovered from ", r)
}
}
func fullName(firstName *string, lastName *string) {
defer recoverName()
if firstName == nil {
panic("runtime error: first name cannot be nil")
}
if lastName == nil {
panic("runtime error: last name cannot be nil")
}
fmt.Printf("%s %s\n", *firstName, *lastName)
fmt.Println("returned normally from fullName")
}
func main() {
defer fmt.Println("deferred call in main")
firstName := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}
8 其他
8.1 反射
反射就是程序能够在运行时检查变量和值,求出它们的类型。
在 Go 语言中,reflect 实现了运行时反射。reflect 包会帮助识别 interface{} 变量的底层具体类型和具体值。
import (
"fmt"
"reflect"
)
type order struct {
ordId int
customerId int
}
func createQuery(q interface{}) {
t := reflect.TypeOf(q)
v := reflect.ValueOf(q)
fmt.Println("Type ", t)
fmt.Println("Value ", v)
}
func main() {
o := order{
ordId: 456,
customerId: 56,
}
createQuery(o)
}
8.2 文件操作
1)fileinfo
//绝对路径
fileInfo , err := os.Stat("/Users/xxx/Documents/1.png")
//相对路径
fileInfo , err = os.Stat("./files/1.docx")
if err !=nil {
fmt.Println("err:" , err.Error())
} else {
//文件名
fmt.Println(fileInfo.Name())
//是否是目录
fmt.Println(fileInfo.IsDir())
//文件尺寸大小
fmt.Println(fileInfo.Size())
//mode 权限
fmt.Println(fileInfo.Mode())
//文件最后修改时间
fmt.Println(fileInfo.ModTime())
}
2)filepath
3)创建
os.MKdir()仅创建一层目录。
os.MKdirAll()创建多层目录。
os.Create(name string) (file *File, err Error根据提供的文件名创建新的文件,返回一个文件对象,如果文件存在,会将其覆盖。
os.NewFile(fd uintptr, name string) *File 根据文件描述符创建相应的文件,返回一个文件对象
os.Open(name string) (file *File, err Error)只读方式打开一个名称为name的文件,函数本质上是在调用os.OpenFile()函数。
os.OpenFile(name string, flag int, perm uint32) (file *File, err Error)
第一个参数:filename,文件名称。第二个参数:mode,文件的打开方式。可同时使用多个方式,用“|”分开。
第三个参数:perm,文件的权限。文件不存在时创建文件,需要指定权限。
os.Close()关闭文件
os.Remove(name string) Error
删除文件名为name的文件
4)文件读写
file.Read(b []byte) (n int, err Error)从文件中开始读取数据,返回值n是实际读取的字节数。如果读取到文件末尾,n为0或err为io.EOF。
file.Write(b []byte) (n int, err Error)写入byte类型的信息到文件
file.WriteAt(b []byte, off int64) (n int, err Error)在指定位置开始写入byte类型的信息
file.WriteString(s string) (ret int, err Error)写入string信息到文件