目录
一、 package
① package 用来组织源码,四多个go源码的集合,代码复用的基础,fmt,os,io
② 每个源码文件开始都必须要申明所属的package
③ python 不需要去申请packa php c# namespace
package main
1.1 在同一文件夹下
在同一个文件夹下 package包必须相同
在同一个文件夹下 代码是可以互相引用的
例如:
pack文件夹下的user.go文件:
package pack
type User struct {
Name string
}
pack文件夹下的person.go文件:
package pack
func P(c User) string { // 在一个文件夹下可以直接用
return c.Name
}
1.2 在不同文件夹下
在不同文件下需要import
例如:
packtext文件夹下的main.go文件:
package main
import (
"fmt"
"go_learning/day8/Pack/pack" //不同文件夹下的相互引用
)
func main() {
u :=pack.User{Name: "go"} //pack为package包名
fmt.Println(pack.P(u))
}
1.3 import引用特殊用法
import (
"fmt"
a"go_learning/day8/Pack/pack" //取别名
)
func main() {
u :=a.User{Name: "go"} //user也要换成u
fmt.Println(a.P(u))
}
_"go_learning/day8/Pack/pack" //暂时不用
."go_learning/day8/Pack/pack" //导入用法,尽量少用
1.4 go.mod命令
1.5gomodules
二、 单元测试
单元测试表格
三、性能测试
四、并发编程
4.1 回忆
一、 进程和线
二、 并行和并发
① 并发:多个线程同时竞争一个位置,竞争到的才可以执行,每一个时间段只有一个线程在执
行。
② 并行:多个线程可以同时执行,每一个时间段,可以有多个线程同时执行。
③ 单核上并发 多核上并行
4.2 goroutine 协程
goroutine 的调度是随机的 每次输出的结果的顺序都不一致
go 函数(参数)
① 主线程不会等协程(当主线程执行完毕 不管协程便停止运行)
② 携程执行完毕后 需要等主线程结束,代码才算结束
解决方法:wg协程计数器
var wg sync.WaitGroup //定义一个全局变量
func test1() {
for i := 1; i <= 10; i++{
fmt.Println("test1() 你好goland", i)
time.Sleep(time.Millisecond * 100) //每隔50ms输出一次//time.Sleep 休眠
}
wg.Done() //协程计数器减1
}
func main() {
wg.Add(1) //协程计数器加1
go test1() //表示开启一个协程
for i := 1; i <= 10; i++{
fmt.Println("main() 你好goland", i)
time.Sleep(time.Millisecond * 50) //每隔50ms输出一次
}
wg.Wait() //等待协程执行完毕
fmt.Println("主线程退出...")
}
//需求:要统计1-12000的数字中哪些是素数? 多线程
/*
1 协程 统计 1-30000
2 协程 统计 30001-60000
3 协程 统计 60001-90000
4 协程 统计 90001-120000
*/
//start (n-1)*30000+1 end n*30000
var wg sync.WaitGroup
func test(n int) {
wg.Done()
for num := (n-1)*300+1; num < n*300; num ++ {
if num > 1 {
flag := true
for i :=2; i < num; i++ {
if num % 2 != 0 {
flag = false
break
}
}
if flag {
fmt.Println("素数", num)
}
}
}
}
func main() {
start := time.Now().Unix()
for i := 1; i < 4; i++{
wg.Add(1)
go test(i)
time.Sleep(time.Millisecond * 50) //排序
}
wg.Wait()
fmt.Println("执行完毕")
end := time.Now().Unix()
fmt.Println("执行时间:", end-start)
}
4.3 CPU 数量
func main() {
//获取当前计算机上面的 Cup 个数
cpuNum := runtime.NumCPU()
fmt.Println("cpuNum=", cpuNum)
//可以自己设置使用多个 cpu
runtime.GOMAXPROCS(cpuNum - 1)
fmt.Println("ok")
}
4.4 channel 管道
① 管道是 Golang 在语言级别上提供的 goroutine 间的通讯方式
② 是一种特殊的类型 引用数据类型(改变副本 原来的也会变)
③ 先入先出(类似队列)
4.4.1 基本语法
① 创建channel
ch := make(chan int, 3)
② 给管道存数据
ch <- 10
③ 获取管道里的内容
a := <- ch
<- ch
④ 管道的类型(引用数据类型)
改变副本 原来的也会变
⑤ 管道的容量和长度
fmt.Printf("值:%v 容量:%v 长度: %v",ch ,cap(ch), len(ch)) //值:0xc000104000 容量:3 长度: 0
⑥ 死锁deadlock(管道阻塞)
写满了 或者 没有数据硬取数据
⑥ 循环遍历
注意管道没有key 只用接收value
管道必须关闭(close(XXX))再for range循环,否则会死锁(新版go没这种情况了)
var ch3 = make(chan int, 10)
for i :=1; i <= 10; i++ {
ch1 <- i
}
close(ch3)
for v :=range ch3 { //注意管道没有key 只用接收value
fmt.Println(v)
}
4.4.2 goroutine - channel
写数据和存数据同时进行
需求 :goroutine 结合 channel 实现统计 1-120000 的数字中那些是素数?
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
//向 intChan 放入 1-120000 个数
func putNum(intChan chan int) {
for i := 2; i <= 120000; i++{
intChan <- i
}
close(intChan)//关闭 intChan
wg.Done()
}
// 从 intChan 取出数据,并判断是否为素数,如果是,就放入到 primeChan
func primeNum(intChan,primeChan chan int, exitChan chan bool) {
for num := range intChan {
flag := true
for i := 2; i < num; i++ {
if num %i == 0 {
flag = false
break
}
}
if flag {
primeChan <- num //将这个数就放入到 primeChan
}
}
exitChan <- true//判断关闭
wg.Done()
}
//打印素数的方法
func printPrime(primeChan chan int) {
for num := range primeChan {
fmt.Println(num)
}
wg.Done()
}
//判断什么时候退出
func exitfn(exitChan chan bool,primeChan chan int) {
for i := 0; i < 8; i++ {
<-exitChan
}
//当我们从 exitChan 取出了 8 个结果,就可以放心的关闭 primeChan
close(primeChan)
wg.Done()
}
func main() {
start := time.Now().Unix()
intChan := make(chan int, 1000)
primeChan := make(chan int, 20000)//放入结果
exitChan := make(chan bool, 8) //标识退出的管道 8个协程
//开启一个协程,向 intChan 放入 1-12000 个数wg.Add(1)
wg.Add(1)
go putNum(intChan)
//开启 4 个协程,从 intChan 取出数据,并判断是否为素数,如果是,就放入到 primeChan
for i := 0; i < 8; i ++ {
wg.Add(1)
go primeNum(intChan, primeChan, exitChan)
}
//判断什么时候退出
wg.Add(1)
go exitfn(exitChan, primeChan)
//打印素数
wg.Add(1)
go printPrime(primeChan)
wg.Wait()
end := time.Now().Unix()
fmt.Println("执行时间:", end - start)
fmt.Println("main线程退出")
}
4.4.3 单向管道
① 限制管道在函数中只能发送或只能接收。
② 在默认情况下下,管道是双向
③只读管道
var fn1 chan <- int //只读管道
④ 只写管道
var fn2 <- chan int //只写管道
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
//写数据
func fn1(ch chan<- int){ //只写的管道
for i := 1; i <= 10; i++{
ch <- i
}
close(ch)
wg.Done()
}
//读数据
func fn2(ch <-chan int) { //只读的管道
for v := range ch {
fmt.Println(v)
}
wg.Done()
}
func main() {
var ch = make(chan int, 10)
wg.Add(1)
fn1(ch)
wg.Add(1)
fn2(ch)
wg.Wait()
}
4.5 select 多路复用
① 可以同时响应多个管道的操
② 使用selsect时候 不需要关闭channel
③ 使用 select 可以解决从管道取数据的阻塞问题
④使用 select 语句能提高代码的可读性。
• 可处理一个或多个 channel 的发送/接收操作。
• 如果多个 case 同时满足,select 会随机选择一个。
• 对于没有 case 的 select{}会一直等待,可用于阻塞 main 函数。
func main() {
//在某些情况下我们需要同时从多个通道中接收数据,可以用golang自带的select
//定义一个管道 10个数据int
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
//定义一个管道 5个string
stringChen := make(chan string, 5)
for i := 0; i < 5; i++ {
stringChen <- "hello" + fmt.Sprintf("%d", i)
}
for {
select {
case v := <-intChan:
fmt.Printf("从 intChen 读取的数据%d\n", v)
time.Sleep(time.Millisecond * 50)
case v := <- stringChen:
fmt.Printf("从 intChen 读取的数据%d\n", v)
time.Sleep(time.Millisecond * 50)
default:
fmt.Printf("数据获取完毕")
return //注意退出
}
}
}
4.6 mutux 并发安全和锁
① 互斥锁
var lock sync.Mutex
lock.Lock() //加锁
lock.Unlock() //解锁
② 读写锁
var rwlock sync.RWMutex
rwlock.RLock() //加锁
rwlock.RUnlock() //解锁
五、 反射
① Go 语言中的变量是分为两部分的:
类型信息:预先定义好的元信息。
值信息:程序运行过程中可动态变化的。
② 任何接口值都由是一个具体类型和具体类型的值两部分组成的。
③ 任意接口值在反射中都可以理解为由 reflect.Type 和 reflect.Value 两 部 分 组 成。
5.1 reflect.Type
var a float32 = 12.5
fmt.Println(reflect.TypeOf(a)) // type:float32
5.1.1 type Name 和 type Kind
var e *float32
getType := reflect.TypeOf(e)
fmt.Println(getType) // type:*float32
fmt.Println(getType.Name()) //Name:
fmt.Println(getType.Kind()) //Kind:ptr
① type Name不常用
② 在 reflect 包中定义的 Kind 类型如下:
Invalid Kind = iota | 非法类型 | Bool | 布尔型 | Int | 有符号整型 |
Int8 | 有符号 8 位整型 | Int16 | 有符号 16 位整型 | int32 | 有符号 32 位整型 |
Int64 | 有符号 64 位整型 | Uint | 无符号整型 | Uint8 | 无符号 8 位整型 |
Uint16 | 无符号 16 位整型 | Uintptr | 指针 | Float32 | 单精度浮点数 |
Float64 | 双精度浮点数 | Complex64 | 64 位复数类型 | Array | 数组 |
Chan | 通道 | Func | 函数 | Interface | 接口 |
Slice | 切片 | Ptr | 指针 | Map | 映射 |
String | 结构体 | UnsafePointer | 底层指针 |
5.2 reflect.ValueOf
① 用反射获取原始值
reflect.Value 类型提供的获取原始值的方法如下:
方法 | 说明 |
---|---|
Interface() interface {} | 将值以 interface{} 类型返回,可以通过类型断言转换为指定类型 |
Int() int64 | 将值以 int 类型返回,所有有符号整型均可以此方式返回 |
Uint() uint64 | 将值以 uint 类型返回,所有无符号整型均可以此方式返回 |
Float() float64 | 将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回 |
Bool() bool | 将值以 bool 类型返回 |
Bytes() []bytes | 将值以字节数组 []bytes 类型返回 |
String() string | 将值以字符串类型返回 |
... |
package main
import (
"fmt"
"reflect"
)
func reflectValue(x interface{}) {
getValue := reflect.ValueOf(x)
getKind := getValue.Kind()
switch getKind {
case reflect.Int64:
fmt.Printf("int类型的原始值是%v\n", getValue.Int() + 10)
case reflect.Float32:
fmt.Printf("Float32类型的原始值是%v\n", getValue.Float() + 10.1)
case reflect.Float64:
fmt.Printf("Float64类型的原始值是%v\n", getValue.Float() + 10.1)
case reflect.String:
fmt.Printf("String类型的原始值是%v\n", getValue.String())
default:
fmt.Printf("还没判断这个类型\n")
}
}
func main() {
var a int64 = 100
var b float32 =12.3
var c string = "你好,golang"
reflectValue(a)
reflectValue(b)
reflectValue(c)
}
② 通过反射修改变量的值:
反射中使用专有的 Elem()方法来获取指针对应的值
package main
import (
"fmt"
"reflect"
)
func reflectSetValue(x interface{}) {
getValue := reflect.ValueOf(x)
fmt.Println(getValue.Kind()) //ptr
fmt.Println(getValue.Elem().Kind()) //int64
if getValue.Elem().Kind() == reflect.Int64 {
getValue.Elem().SetInt(123)
}
}
func main() {
var a int64 = 100
reflectSetValue(&a)
fmt.Println(a)
}
5.3 结构体反射
5.3.1 表格
reflect.Type 中与获取结构体成员相关的的方法如下表所示。
( 也就是 getType := reflect.TypeOf(s) getType.XXX )XXX为下面的内容
方法 | 说明 |
---|---|
Field(i int) StructField | 根据索引,返回索引对应的结构体字段的信息 |
NumField() int | 返回结构体成员字段数量 |
FieldByName(name string) StructField, bool | 根据给定字符串返回字符串对应的结构体字段的信息 |
FieldByIndex(index []int) StructField | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息 |
FieldByNameFunc(match func(string) bool) (StructField,bool) | 根据传入的匹配函数匹配需要的字段 |
NumMethod() int | 返回该类型的方法集中方法的数目 |
Method(int) Method | 返回该类型方法集中的第 i 个方法 |
MethodByName(string) Method, bool | 根据方法名返回该类型方法集中的方法 |
(注:是看前面的 后面是.之后的内容/接受的参数)
StructField 类型用来描述结构体中的一个字段的信息。
( 也就是 getType := reflect.TypeOf(s) field0 := getType.Field field0.XXX )
方法 | 说明 |
---|---|
Name string | Name 是字段的名字 |
PkgPath string | PkgPath 是非导出字段的包路径,对导出字段该字段为"" |
Type Type | 字段的类型 |
Tag StructTag | 字段的标签 |
Offset uintptr | 字段在结构体中的字节偏移量 |
Index []int | 用于 Type.FieldByIndex 时的索引切片 |
Anonymous bool | 是否匿名字段 |
5.3.2 获取 结构体内容
package main
import (
"fmt"
"reflect"
)
//student 结构体
type Student struct {
Name string `json:"name" form:"username"` //Tag标签可以定义多个
Age int `json:"age"`//Tag标签
Score int `json:"score"`
}
//打印字段
func PrintStructField(s interface{}) {
getType := reflect.TypeOf(s)
getValue := reflect.ValueOf(s)
getKind := getType.Kind()
if getKind != reflect.Struct && getType.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体")
return
}
//1、通过类型变量里面的Field可以获取结构体的字段
field0 := getType.Field(0) //获取的就是结构体 0表示返回的第0个字段(结构体的第0项)
fmt.Println(field0.Name)
fmt.Println(field0.Type)
fmt.Println(field0.Tag.Get("json")) //Get获取指定的标签
fmt.Println(field0.Tag.Get("form"))
fmt.Println("--------------------------------")
//2、通过类型变量里面的 FieldByName 可以获取结构体的字段
field1, _ := getType.FieldByName("Age")
fmt.Println(field1.Name)
fmt.Println(field1.Type)
fmt.Println(field1.Tag.Get("json"))
//3、获取到该结构体有几个字段
fieldCount := getType.NumField()
fmt.Println("结构体有:", fieldCount, "个属性")
fmt.Println("--------------------------------")
//4、获取结构体属性对应的值
fmt.Println(getValue.FieldByName("Name"))
for i :=0; i < fieldCount; i++{
fmt.Printf("属性名称%v, 属性值%v\n", getType.Field(i).Name, getValue.Field(i))
}
}
func main() {
stu1 := Student{
Name: "小明",
Age:
15,
Score: 98,
}
PrintStructField(stu1)
}
5.3.3 获取/修改 结构体方法中的内容
package main
import (
"fmt"
"reflect"
)
//student 结构体
type Student struct {
Name string `json:"name" form:"username"` //Tag标签可以定义多个
Age int `json:"age"`//Tag标签
Score int `json:"score"`
}
func (s Student) GetInfo() string {
var str = fmt.Sprintf("姓名:%v 年龄:%v 成绩:%v", s.Name, s.Age, s.Score)
fmt.Println(str)
return str
}
func (s *Student) SetInfo(name string, age int, score int) {
s.Name = name
s.Age = age
s.Score = score
}
func (s *Student) Print() {
fmt.Println("打印方法...")
}
//方法
func PrintStructFn(s interface{}) {
getType := reflect.TypeOf(s)
getValue := reflect.ValueOf(s)
getKind := getType.Kind()
if getKind != reflect.Struct && getType.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体")
return
}
//1、通过类型变量里面的Method可以获取结构体的字段(不常用)
method0 := getType.Method(0) //0和结构体方法的顺序没关系,和结构体方法的ASCII有关
fmt.Println(method0.Name)
fmt.Println(method0.Type)
fmt.Println("--------------------------------")
//2、通过类型变量里面的 MethodByName 可以获取结构体方法的字段
method1, _ := getType.MethodByName("Print")
fmt.Println(method1.Name)
fmt.Println(method1.Type)
fmt.Println("--------------------------------")
//3、执行方法
getValue.MethodByName("Print").Call(nil) //Call表示执行 不需要传参写nil
aaa :=getValue.MethodByName("GetInfo").Call(nil)
fmt.Println(aaa)
//4、执行方法,传入参数
var params []reflect.Value
params = append(params,reflect.ValueOf("李四"))
params = append(params,reflect.ValueOf(23))
params = append(params,reflect.ValueOf(99))
getValue.MethodByName("SetInfo").Call(params)
bbb :=getValue.MethodByName("GetInfo").Call(nil)
fmt.Println(bbb)
//3、通过NumMethod获取到该结构体方法有几个字段
MethodCount := getType.NumMethod()
fmt.Println("结构体方法有:", MethodCount, "个数量")
}
func main() {
stu1 := Student{
Name: "小明",
Age:
15,
Score: 98,
}
PrintStructFn(&stu1)
}
5.3.4 修改 结构体内容
package main
import (
"fmt"
"reflect"
)
//student 结构体
type Student struct {
Name string `json:"name" form:"username"` //Tag标签可以定义多个
Age int `json:"age"`//Tag标签
Score int `json:"score"`
}
//修改
func reflectChangeStruct(s interface{}) {
getType := reflect.TypeOf(s)
getValue := reflect.ValueOf(s)
if getType.Kind() != reflect.Ptr {
fmt.Println("传入的不是指针类型")
return
}else {
if getType.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体指针类型")
return
}
}
//修改结构体属性的值
name := getValue.Elem().FieldByName("Name")
name.SetString("小李")
age := getValue.Elem().FieldByName("Age")
age.SetInt(22)
}
func main() {
stu1 := Student{
Name: "小明",
Age:
15,
Score: 98,
}
reflectChangeStruct(&stu1)
fmt.Printf("%v\n", stu1) //{小李 22 98}
fmt.Printf("%#v\n", stu1) //main.Student{Name:"小李", Age:22, Score:98}
}
六、 文件 目录操作
6.1 文件读取
6.1.1 只读形式打开文件
只读方式打开文件 file,err := os.Open()
file, err := os.Open ("/home/wolf/桌面/test.txt")
defer file.Close()
if err != nil {
fmt.Println(err)
return
}
6.1.2 read
① Read读取文件
1、只读方式打开文件 file,err := os.Open()
2、读取文件 file.Read()
3、关闭文件流 defer file.Close()
var strSlice []byte
var tempSlice = make([]byte, 128)
for {
n, err := file.Read(tempSlice) //n是读取的字节数
if err == io.EOF { //err==io.EOF表示读取完毕
fmt.Println("读取成功")
break
}
if err != nil {
fmt.Println("读取失败")
return
}
strSlice = append(strSlice, tempSlice[:n]...)
}
fmt.Println(string(strSlice))
② bufio 读取文件
1、只读方式打开文件 file,err := os.Open()
2、创建reader对象 reader := bufio.NewReader(file)
3、ReadString读取文件 line, err := reader.ReadString('\n')
4、关闭文件流 defer file.Close()
var line string
reader := bufio.NewReader(file)
for {
temp, err := reader.ReadString('\n') //表示一次读取以行
if err == io.EOF {
fmt.Println("读取成功")
line +=temp
break
}
if err != nil {
fmt.Println("读取失败")
return
}
line +=temp
}
fmt.Println(line)
}
③ ioutil 读取文件 (新版编译器提示已删除的内容)
打开关闭文件的方法它都封装好了只需要一句话就可以读取
ioutil.ReadFile("./main.go")
func main() {
byetStr, err := ioutil.ReadFile("/home/wolf/桌面/test.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(byetStr))
}
6.2 文件写入
6.2.1 可写...的方式打开文件
name:要打开的文件名
flag:打开文件的模式。模式有以下几种:
perm:文件权限,一个八进制数。r(读)04,w(写)02,x(执行)01。
file, err := os.OpenFile("/home/wolf/桌面/test.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
模式 | 含义 |
---|---|
os.O_WRONLY | 只写 |
os.O_CREATE | 创建文件 |
os.O_RDONLY | 只读 |
os.O_RDWR | 读写 |
os.O_TRUNC | 清空 |
os.O_APPEND | 追加 |
6.2.2 write
①
1、打开文件 file, err := os.OpenFile("C:/test.txt", os.O_CREATE|os.O_RDWR, 0666)
2、写入文件
file.Write([]byte(str)) //写入字节切片数据
file.WriteString("直接写入的字符串数据") //直接写入字符串数据
3、关闭文件流 file.Close()
//写入字节切片数据
//str := "哈哈哈哈\n"
//file.Write([]byte(str))
//直接写入字符串数据
for i :=0; i < 10; i++ {
file.WriteString("直接写入的字符串数据" + strconv.Itoa(i) + "\r\n")
}
② bufio 写入文件
1、打开文件 file, err := os.OpenFile("C:/test.txt", os.O_CREATE|os.O_RDWR, 0666)
2、创建writer对象 writer := bufio.NewWriter(file)
3、将数据先写入缓存 writer.WriteString("你好golang\r\n")
4、将缓存中的内容写入文件 writer.Flush()
5、关闭文件流 file.Close()
writer := bufio.NewWriter(file)
for i := 0; i < 10; i++ {
writer.WriteString("你好golang\r\n")
}
writer.Flush()
③ ioutil 写入文件 (新版编译器提示已删除的内容)
str := "hello golang"
err := ioutil.WriteFile("C:/test.txt", []byte(str), 0666)
func main() {
str := "hello golang"
err := ioutil.WriteFile("/home/wolf/桌面/test.txt", []byte(str), 0666)
if err != nil {
fmt.Println(err)
return
}
}
6.3 文件复制
① ioutil 复制文件 (新版编译器提示已删除的内容)
input, err := ioutil.ReadFile(srcFileName)
err = ioutil.WriteFile(dstFileName, input, 0644)
func copy(srcFileName string, dstFileName string) (err error) {
byteStr, err1 := ioutil.ReadFile(srcFileName)
if err1 != nil {return err1}
err2 :=ioutil.WriteFile(dstFileName, byteStr, 0666)
if err2 != nil {return err2}
return nil
}
func main() {
src := "/home/wolf/桌面/视频哦.mp4"
dst := "/home/wolf/桌面/1111.mp4"
err := copy(src, dst)
if err != nil {fmt.Println(err); return}
fmt.Println("复制文件成功")
}
② 文件流的方式复制
source, _ := os.Open(srcFileName)
destination, _ := os.OpenFile(dstFileName, os.O_CREATE|os.O_WRONLY, 0666)
n, err := source.Read(buf)
destination.Write(buf[:n]);
func copy(srcFileName string, dstFileName string) (err error) {
sFile, err1 := os.Open(srcFileName)
defer sFile.Close() //不关闭会造成内存泄漏
dFile, err2 := os.OpenFile(dstFileName, os.O_CREATE| os.O_WRONLY, 066)
defer dFile.Close()
if err1 != nil {return err1}
if err2 != nil {return err2}
var tempSlice = make([]byte, 1280) //大文件尽可能大一些
for {
n1, err3 := sFile.Read(tempSlice) //读入切片
if err3 == io.EOF {break}
if err3 != nil {return err3}
_, err4 := dFile.Write(tempSlice[:n1]) //从切片中取出来 注意要[:n1]
if err4 != nil {return err4}
}
return nil
}
func main() {
src := "/home/wolf/桌面/视频哦.mp4"
dst := "/home/wolf/桌面/1111.mp4"
err := copy(src, dst)
if err != nil {fmt.Println(err); return}
fmt.Println("复制文件成功")
}
6.4 文件重命名
err := os.Open(old.txt, new.txt)//只能同盘操作
err := os.Rename("/home/wolf/桌面/1111.mp4","/home/wolf/桌面/视频哦.mp4")
6.5 目录添加
err := os.Mkdir("./abc", 0666)
err := os.MkdirAll("dir1/dir2/dir3", 0666) //创建多级目录
① 创建单级目录
err := os.Mkdir("/home/wolf/桌面/1111", 0666)
② 创建多级目录
err := os.MkdirAll("/home/wolf/桌面/1111/222/333/444", 0666)
6.6 删除文件/目录
err := os.Remove("t.txt")
err := os.RemoveAll("aaa")
① 删除文件
err := os.Remove("/home/wolf/桌面/test.txt")
② 删除一个目录
err := os.Remove("/home/wolf/桌面/111")
③ 删除多个目录
err := os.RemoveAll("/home/wolf/桌面/111/2222/3333")