go文件
輸入流:從文件到go程序內存,讀文件的過程
輸出流:從go程序內存到文件,寫文件的過程
- 打開文件,返回文件指針和錯誤
file, err := os.Open("文件路徑")
- 關閉文件,返回錯誤信息,經常與defer連用
注意:退出函數時要及時關閉,否則會有內存泄漏
err = os.Close()
- 讀取文件
- 帶緩存的讀取方式,需要打開和關閉文件
reader := bufio.NewReader(file)
str, err := reader.ReadString('\n') //讀到換行就結束
- 一次性讀取到內存,適用於文件不大的情況
# 不需要打開和關閉文件,被封裝在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協程
併發:多線程程序在單核上運行
並行:多線程程序在多核上運行
- 特點
- 有獨立的棧空間
- 共享程序堆空間
- 調度有用戶控制
- 協程是輕量級的線程
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
特點:
- 先進先出【FIFO】
- 線程安全,無需加鎖
聲明: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