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

该博客围绕Go语言开发展开,介绍了package的使用,包括同一和不同文件夹下的引用、import特殊用法等;还涉及单元测试、性能测试。详细阐述了并发编程,如goroutine协程、channel管道等,以及反射和文件目录操作,如文件读写、复制、重命名等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、 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")

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值