go语言学习笔记4(文件,管道,map)

go文件

输入流:从文件到go程序内存,读文件的过程
输出流:从go程序内存到文件,写文件的过程

  • 打开文件,返回文件指针和错误
    在这里插入图片描述
file, err := os.Open("文件路径")
  • 关闭文件,返回错误信息,经常与defer连用

注意:退出函数时要及时关闭,否则会有内存泄漏
在这里插入图片描述

err = os.Close()
  • 读取文件
  1. 带缓存的读取方式,需要打开和关闭文件
reader := bufio.NewReader(file)
str, err := reader.ReadString('\n')  //读到换行就结束
  1. 一次性读取到内存,适用于文件不大的情况
# 不需要打开和关闭文件,被封装在ReadFile
content, err := ioutil.ReadFile(file)
  • 文件演示
import (
	"os"
	"fmt"
	"io"
	"bufio"
	"io/ioutil"
)

func main() {
	// open file
	file, err := os.Open("c:/test.txt")
	if err != nil{
		fmt.Println("open file error")
	}
	
	fmt.Println(file)   //输出文件地址
	
	// close file
	defer err = file.Close()
	if err != nil{
		fmt.Println("close file error")
	}

	// read file method 1
	reader := bufio.NewReader(file)
	for {         // 直接循环
		str, err := reader.ReadString('\n')  //读到换行就结束
		if err == io.EOF {  //读到文件末尾
			break
		}
	}

	// read file method 2
	file = "d:/test.txt"
	content, err := ioutil.ReadFile(file)
	if err != nil{
		fmt.Println("read file error")
	}
	fmt.Println(content)  //默认是byte[],全是数字
	fmt.Println(string(content))
}
  • 写文件
    在这里插入图片描述
import (
	"bufio"
)

func main() {
	fileName := "c:/test.txt"
	file, err := os.OpenFile(fileName , os.O_WRONLY | os.O_CREATE, 666)
	if err != nil{
		fmt.Println("open file error")
	}
	
	str := "Hello Amber!!!\n"  //换行,有的编辑器\r\n
	writer := bufio.NewWriter(file)
	writer.WriteString(str)  //此时仅把数据写入缓存
	writer.Flush()  //将缓存内容写入文件
	
	defer file.Close()
}

os.O_WRONLY | os.O_CREATE :写文件,没有就创建
os.O_WRONLY | os.O_APPEND :写文件,不会覆盖,追加
os.O_RDWR :可读可写

  • 文件拷贝
func main() {
	file1 = "c:/origin.txt"
	file2 = "c:/copy.txt"
	data, err := ioutil.ReadFile(file1)
	err := ioutil.WriteFile(file2, data, 666)
}
  • 判断文件是否存在
func main() {
	_, err := os.Stat("c:/origin.txt")
	if err == nil{
		return true //文件/目录存在
	}
	if os.IsNotExist(err){
	return false  //文件/目录不存在
	}
}
  • 图片/电影拷贝
import (
	"io/util"
	"bufio"
	"os"
)

func CopyFile(dstName string, srcName string) (written int64, err error){
	srcFile, err := os.Open(srcName)
	if err != nil{
		fmt.Println("open file error")
	}
	reader := bufio.NerReader(srcFile)
	
	defer srcFile.Close()

	// Open用于打开读文件,OpenFile是打开写文件
	dstFile, err := os.OpenFile(dstName, os.O_WRONLY | os.O_CREATE, 666)
	if err != nil{
		fmt.Println("open file error")
	}
	writer := bufio.NewWriter(dstFile)
	
	defer dstFile.Close()
	
	//copy
	return io.Copy(writer, reader)
}

func main() {
	srcFile := "c:/cat.jpg"
	dstFile := "d:/cat.jpg"
	_, err := CopyFile(dstFile, srcFile)
	if err != nil{
		fmt.Println("copy error")
	}elsd{
		fmt.Println("copy success")
	}
}

获取命令行参数
  • DOS >>> copy a.txt b.txt
import(
	"os"
)

func main() {
	for i, val := range os.Args {
		fmt.Println("args[%v]=%v", i, val)
	}
}
  • 通过flag包获取命令行>>> mysql -u root -p root -h localhost
import (
	"flag"
)

func main() {
	var user string
	var pwd string
	var host string
	var port int

	// &user:获取-u后面的输入的参数
	//"u":-u指定参数
	// "":默认值
	//"用户名,默认为空":注释
	flag.StringVar(&user, "u", "", "用户名,默认为空")
	flag.StringVar(&pwd, "p", "", ",密码默认为空")
	flag.StringVar(&host, "h", "localhost", "主机")
	flag.StringVar(&port, "port", 3306, "端口")

	flag.Parse()	 //必须进行转换
	fmt.Println("user=%v,pwd=%v,host=%v,port=%v", user, pwd, host, port)
}
json

应用场景:网络传输前后台都是以json格式

验证jason格式化网址:www.json.cn

  • 序列化:把key-vlue类型的数据如数组,切片序列化成json字符串
import (
	"encoding/json"
)

type Student struct {
	Name string
	Age int
}

func main() {
	stu := Student{"amber", 17}
	
	// 序列化,传递的是地址&stu否则无法改变stu的值,默认值拷贝,返回值是byte[]
	data, err := json.Marshal(&stu)
	if err != nil{
		fmt.Println("序列化fail")
	}
	fmt.Println(string(data))
}
  • json反序列化成struct
import (
	"encoding/json"
)

type Student struct {
	Name string
	Age int
}

func main() {
	str := "{Name: "amber", "Age": 16}"
	var stu Student 
	
	// 第一个参数是转换前的[]byte类型,第二个参数是被转换的数据
	err := ejson.Unmarshal([]byte(str), &stu)
	fmt.Println(stu)
}
go单元测试

控制台:go test -v 带-v代表正确错误都有日志,没有-v正确不会有日志显示在终端
测试单个文件:go test one_test.go
测试单个方法:go test -v -test.run TestUpper

文件名必须以_test.go结尾

函数名必须以Test开头

testing框架:将以test.go文件引入,main函数调用所有以Test开头函数

PASS 代表成功,FAILURE代表失败

import (
	"testing"  //引入go testing的框架
)

// 函数名必须以Test开头,其后的第一个字母不能是a-z
func TestUpper(t *testing.T) {
	res := Upper(10)
	if res != 55{
		t.Fatalf("error")   //停止程序,记录log
	}
	t.Logf("success")
}
goroutine协程

并发:多线程程序在单核上运行
并行:多线程程序在多核上运行
在这里插入图片描述

  • 特点
  1. 有独立的栈空间
  2. 共享程序堆空间
  3. 调度有用户控制
  4. 协程是轻量级的线程
func test() {
	for i :=0; i < 10; i++{
		fmt.Println("test hello world")
	}
}

func main() {
	go test()   //开启协程,主线程和test交叉执行
	for i :=0; i < 10; i++{
		fmt.Println("main hello world")
	}
}

遇到go关键字时,主线程会再开出一个分支执行go后面的方法,主线程仍继续执行,若主线程执行完毕,分支线程无论是否结束都要结束

主线程比较耗费资源,分支线程是轻量级的
在这里插入图片描述

  • MPG模式
    M:操作系统的主线程
    P:协程上下文环境(程序运行所需的资源,比如cpu分配)
    G:协程

举个🐾:现有ABCD四个客人点菜,A点了10个炒菜,厨师做菜的时候BCD就一直等着,但当厨师在做炖汤的时候,可以把B的菜先炒了,计算机可以来回在A和B之间切换

import "runtime"

func main() {
	cpuNum := runtime.NumCPU()
	fmt.Printf("电脑上CPU个数=%v", cpuNum)

	//设置使用的CPU个数
	runtime.GOMAXPROCS(cpuNum - 1)
}

Question🐣
当多个协程同时对数据库写入时,会报错

解决方案:1,管道,2,互斥锁

import "sync"

//lock时全局互斥锁,Mutex是结构体,里面有lock和unlock2个方法
var lock sync.Mutex

//在执行写入操作之前
lock.lock()
map[0] = res
//在执行完写入操作之后
lock.Unlock()
go管道channel

特点:

  1. 先进先出【FIFO】
  2. 线程安全,无需加锁

声明:var 变量名 chan 数据类型

初始化:make
在这里插入图片描述

func main() {
	var intChan chan int
	
	// make创建存放3个int类型的管道
	intChan = make(chan int, 3)
	fmt.println(intChan)  //输出的是地址,&intChan也是地址

	//向管道写入数据
	intChan<- 100
	num := 200
	intChan<- num
	fmt.println(len(intChan), cap(intChan)) 
	// 注意,make时指定的容量就是3,如果写入超过3个会报错

	// 取出数据,若数据已全部被取出还在取会报错
	var num2 int
	num2 = <-intChan

	// 仅把数据扔出,不给任何变量
	 <-intChan
}

在这里插入图片描述
取出的数据类型也是interface,需要断言
在这里插入图片描述

  • 管道的关闭
var intChan chan int
intChan = make(chan int, 3)
intChan<- 100
close(intChan) //关闭管道
intChan<- 200  //报错,管道关闭不允许写入
n:= <-intChan  //可以正常读取
  • 遍历管道
    ①:遍历时,如果channel没有关闭,会出现deadlock错误
    ②:遍历时,如果channel关闭,正常遍历

注意:遍历管道时不能使用len(),因为每取出一个数据,管道的长度是会变的

intchan2 := make(chan int, 100)
for i:=0;i<100;i++ {
	intchan2<- i
}
close(intchan2)   //关闭
for i range intchan2 {
	fmt.Println(i)   //直接取出
}
  • 协程和管道结合

问题描述:定义2个协程,一个写入50个数据,一个读出50个数据,并且保证在主进程结束之前完成读写操作

思路:为防止主线程在协程之前结束,需要在定义一个管道专门用来放读完的标志

在这里插入图片描述

func write(intChan chan int) {
	for i :=1;i <=50;i++{
		intChan <- i
	}
	close(intChan)
}

func read(intChan chan int, boolChan chan bool) {
	for {
		val, ok := <- intChan
		if !ok {
			fmt.Println("read data=%v", val)
		}
	}
	boolChan <- true
	close(boolChan)
}

func main() {
	intChan := make(chan int, 50)
	exitChan := make(chan bool, 1)

	go write(intChan)
	go read(intChan, exitChan)
	
	for {
		_, ok := <- exitChan 
		if !ok {
			break
		}
	}
}
  • 管道阻塞
    不管读的速度快还是写的速度块都不会发生死锁,因为编译器会分析
for {
	select {  //如果管道没有关闭,也可以遍历数据,不会被阻塞,若管道里没有数据自动匹配下一个case
		case v := <-intChan:
			fmt.Println(v)
		case v := <-stringChan:
			fmt.Println(v)
		default :
			break  //只能跳到select,无法跳出for循环
			return  // 可以
	}
}

①:管道声明为双向 var 管道名 chan int
②:管道声明为只写 var 管道名 chan <- int
③:管道声明为只读 var 管道名 <- chan int

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值