第四章 并发编程和工程管理(package 单元测试 性能测试 并发编程 反射 文件目录操作)

目录

一、 package

1.1 在同一文件夹下

1.2 在不同文件夹下

1.3 import引用特殊用法

1.4 go.mod命令

1.5gomodules

二、 单元测试

三、性能测试

四、并发编程

4.1 回忆

4.2 goroutine 协程

4.3 CPU 数量

4.4 channel 管道

4.4.1 基本语法

4.4.2 goroutine - channel

4.4.3 单向管道

4.5 select 多路复用

4.6 mutux 并发安全和锁

五、 反射

5.1 reflect.Type

5.1.1 type Name 和 type Kind

5.2 reflect.ValueOf

5.3 结构体反射

5.3.1 表格

5.3.2 获取 结构体内容

5.3.3 获取/修改 结构体方法中的内容

5.3.4 修改 结构体内容

六、 文件 目录操作

6.1 文件读取

6.1.1 只读形式打开文件

6.1.2 read

6.2 文件写入

6.2.1 可写...的方式打开文件

6.2.2 write

6.3 文件复制

6.4 文件重命名

6.6 删除文件/目录


一、 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双精度浮点数Complex6464 位复数类型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  stringName 是字段的名字
PkgPath stringPkgPath 是非导出字段的包路径,对导出字段该字段为""
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")

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值