對於文件,咱們並不陌生,文件是數據源(保存數據的地方)的一種,好比你們常用的word文檔,txt文件,Excel文件...等等都是文件。文件最主要的做用就是保存數據,它既能夠保存一張圖片,也能夠保存視頻,聲音......
文件在程序中是以流的形式來操做的。golang
流:數據在數據源(文件)和程序(內存)之間經歷的路徑
輸出流:數據從程序(內存)到數據源(文件)的路徑
輸入流:數據從數據源(文件)到程序(內存)的路徑
輸入與輸出都是相對於內存而言的,從內存向外流就是輸出,從外部向內存流就是輸入緩存
在Go中,咱們操做文件的方法在os包中,會常用到os.File結構體 Go語言標準庫文檔app
package main import ( "fmt" "os" ) func main() { //打開文件(/Users/xxx/Go/src/file.txt) //概念說明:file的叫法 //1.file 叫 file對象 //2.file 叫 file指針 //3.file 叫 file文件句柄 file, err := os.Open("/Users/itbsl/Go/src/file.txt") if err != nil { fmt.Println("文件打開失敗,緣由是:", err) //os.Exit(0) } defer func() { //文件及時關閉 err = file.Close() if err != nil { fmt.Println("文件關閉失敗,緣由是", err) } }() }
使用Read()函數按照字節讀函數
package main import ( "fmt" "io" "os" ) func main() { file, err := os.Open("./test.txt") if err != nil { fmt.Printf("open file failed, err:%v\n", err) return } defer func() { err = file.Close() if err != nil { fmt.Printf("close file failed, err:%v\n", err) } }() var content []byte var tmp = make([]byte, 128) for { n, err := file.Read(tmp) //爲何是tmp[:n]而不是tmp[:]? //由於當讀取到最後一行的內容長度不足tmp的長度的時候 //新讀取的內容只會覆蓋前半部分上次讀取到的tmp的內容, //後半部分仍是上一次讀取的內容,若是用tmp[:]就會致使 //後半部分久內容又會被從新賦值一次,這實際上是錯誤的 content = append(content, tmp[:n]...) if err == io.EOF {//讀到文件末尾 break } } fmt.Printf("讀取出來的內容爲:\n") fmt.Printf("%q\n", string(content)) }
讀取文件內容並顯示在終端,將文件內容一次性讀取到終端,適用於文件不大的狀況。測試
package main import ( "fmt" "io/ioutil" ) func main() { //打開文件,文件路徑相對於GOPATH開始,或者寫全路徑(/Users/xxx/Go/src/file.txt) file, err := ioutil.ReadFile("src/file.txt") if err != nil { fmt.Println("文件打開失敗,緣由是:", err) } fmt.Printf("%s", string(file)) }
讀取文件的內容並顯示在終端(帶緩衝區的方式),使用os.Open
, file.Close
,bufio.NewReader
,reader.ReadString
函數和方法。適合讀取大文件
1.使用ReadBytes方法
代碼1:3d
package main import ( "bufio" "fmt" "io" "log" "os" ) func main() { file, err := os.Open("./test.txt") if err != nil { log.Fatalf("open file failed, err: %v\n", err) } defer func() { err = file.Close() if err != nil { log.Fatalf("close file failed, err: %v\n", err) } }() //定義變量result用來存儲讀取結果 var result string //建立一個帶有緩衝區的reader reader := bufio.NewReader(file) for { buf, err := reader.ReadBytes('\n') if err != nil && err == io.EOF { //EOF表明文件的末尾 //注意:爲何要判斷err是否等於io.EOF? //由於存在這種狀況,文件有內容的最後那一行尾部沒有換行 //當使用ReadBytes或者ReadString方法按照'\n'換行讀取時,讀到尾部沒有換行的這種狀況時就會報io.EOF錯誤 //此時buf是讀取到了內容的,若是忽略掉了,那麼最終的讀取結果會少了最後一行的內容 result += string(buf) break } result += string(buf) } fmt.Println(result) }
代碼2:指針
package main import ( "bufio" "fmt" "io" "log" "os" ) func main() { file, err := os.Open("./test.txt") if err != nil { log.Fatalf("open file failed, err: %v\n", err) } defer func() { err = file.Close() if err != nil { log.Fatalf("close file failed, err: %v\n", err) } }() //定義變量result用來存儲讀取結果 var result string //建立一個帶有緩衝區的reader reader := bufio.NewReader(file) for { buf, err := reader.ReadBytes('\n') if err != nil { if err == io.EOF { //EOF表明文件的末尾 //注意:爲何要判斷err是否等於io.EOF? //由於存在這種狀況,文件有內容的最後那一行尾部沒有換行 //當使用ReadBytes或者ReadString方法按照'\n'換行讀取時,讀到尾部沒有換行的這種狀況時就會報io.EOF錯誤 //此時buf是讀取到了內容的,若是忽略掉了,那麼最終的讀取結果會少了最後一行的內容 result += string(buf) break } else { log.Fatalf("ReadBytes failed, err: %v\n", err) } } result += string(buf) } fmt.Println(result) }
2.ReadString方法code
package main import ( "bufio" "fmt" "io" "os" ) func main() { //打開文件 file, err := os.Open("./files/test.txt") if err != nil { fmt.Println("文件打開失敗,緣由是:", err) return } //當函數退出時,要及時的關閉file defer func() { //文件及時關閉 err = file.Close() if err != nil { fmt.Println("文件關閉失敗,緣由是", err) } }() //建立一個 *Reader,是帶緩衝的 reader := bufio.NewReader(file) var result string //循環讀取文件內容 for { str, err := reader.ReadString('\n') //讀到一個換行就結束 result += str if err == io.EOF {//io.EOF表明文件的末尾 //注意:若是文件最後一行文字沒有換行,則會一直讀取到文件末尾, //因此即便是判斷讀到了文件末尾,也要把讀取的內容輸出一下 break } } fmt.Println(result) }
第二個參數:文件代開模式(能夠組合);第三個參數:權限控制(如0755)
orm
package main import ( "fmt" "os" ) func main() { //1.建立文件file.txt file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE, 0755) if err != nil { fmt.Println("文件打開/建立失敗,緣由是:", err) return } defer func() { err = file.Close() if err != nil { fmt.Println("文件關閉失敗,緣由是:", err) } }() //寫入數據 var str = "暗黑西遊獅駝嶺,鬥打敗佛孫悟空。\n" for i := 0; i < 5; i++ { file.WriteString(str) } }
1.使用WriteAt()搭配Seek()方法實現寫文件功能視頻
package main import ( "io" "log" "os" ) func main() { file, err := os.OpenFile("./test.txt", os.O_RDWR|os.O_CREATE, 0755) if err != nil { log.Fatalf("open file failed, err: %v\n", err) } defer func() { err = file.Close() if err != nil { log.Fatalf("close file failed, err: %v\n", err) } }() //Seek(): 修改文件的讀寫指針位置. //參數1: 偏移量. 正:向文件尾部偏移, 負:向文件頭部偏移 //參數2: 偏移起始位置 // io.SeekStart: 文件起始位置 // io.SeekCurrent: 文件當前位置 // io.SeekEnd: 文件結尾位置 //返回值:表示從文件起始位置,到當前文件讀寫指針位置的偏移量。 //WriteAt(): 在文件指定偏移位置,寫入[]byte,一般搭配Seek() //參數1: 待寫入的數據 //參數2: 偏移量 //返回: 實際寫出的字節數 for i := 0; i < 5; i++ { offset, _ := file.Seek(-3, io.SeekEnd) _, _ = file.WriteAt([]byte("你好"), offset) } }
注意: 因爲使用的OpenFile函數打開的文件,因此在選擇打開模式的時候不能選擇os.O_APPEND
模式,由於該模式表示的是在文件末尾追加,這與WriteAt在指定的位置寫是想衝突的,雖然我在測試的時候加上os.O_APPEND
模式並無報錯,可是代碼執行完以後發現,想要寫入的內容並無真正的寫入到文件中。
寫入前
寫入後
2.一次性寫文件
package main import ( "io/ioutil" "log" ) func main() { str := "hello樹先生" //若是文件已存在,則會清空原來的內容,寫入新內容,若是文件不存在,則會建立文件並寫入內容 err := ioutil.WriteFile("./test.txt", []byte(str), 0755) if err != nil { log.Fatalf("寫入文件錯誤,錯誤爲:%v\n", err) } }
3.使用帶緩衝的方式寫文件
package main import ( "bufio" "fmt" "os" ) func main() { //1.建立文件file.txt file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0755) defer func() { err = file.Close() if err != nil { fmt.Println("文件關閉失敗,緣由是:", err) } }() if err != nil { fmt.Println("文件建立失敗,緣由是:", err) return } //寫入數據 var str = "你好,世界\n" //寫入時,使用帶緩存的*Writer writer := bufio.NewWriter(file) for i := 0; i < 5; i++ { writer.WriteString(str) } //由於writer是帶緩存的,所以在調用writeString方法時,其實內容是先寫入到緩存 //所以須要調用Flush方法,將緩存數據寫入到文件中,不然文件中會丟失數據 writer.Flush() }
package main import ( "fmt" "io/ioutil" ) func main() { //打開文件,文件路徑相對於GOPATH開始,或者寫全路徑(/Users/xxx/Go/src/file.txt) data, err := ioutil.ReadFile("src/1.txt") if err != nil { fmt.Println("文件打開失敗,緣由是:", err) } err = ioutil.WriteFile("src/2.txt", data, 0755) if err != nil { fmt.Println("文件寫入失敗,緣由是:", err) } }
package main import ( "bufio" "fmt" "os" ) func main() { var s string var reader = bufio.NewReader(os.Stdin) s, _ = reader.ReadString('\n') fmt.Printf("讀取到的內容爲:%s\n", s) }
Go判斷文件或文件夾是否存在的方法爲使用os.Stat()函數返回的錯誤值進行判斷:
(1)若是返回的錯誤爲nil,說明文件或文件夾存在
(2)若是返回的類型使用os.IsNotExist()判斷爲true,說明文件或文件夾不存在
(3)若是返回的錯誤爲其它類型,則不肯定是否存在
package main import ( "fmt" "os" ) func main() { isExist, err := isFileExists("src/sfile.txt") if err != nil { fmt.Println("發生錯誤:", err) } if isExist { fmt.Println("存在") } else { fmt.Println("不存在") } } //判斷文件或者目錄是否存在 func isFileExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err }
io.Copy方法
package main import ( "fmt" "io" "os" ) func CopyFile(srcFileName string, dstFileName string) (int64, error) { //源文件處理 srcFile, err := os.Open(srcFileName) defer func() { err = srcFile.Close() if err != nil { fmt.Println("源文件關閉失敗,緣由是:", err) } }() if err != nil { fmt.Println("源文件打開失敗,緣由是:", err) return 0, err } //目標文件處理 dstFile, err := os.OpenFile(dstFileName, os.O_CREATE | os.O_WRONLY, 0755) defer func() { err = dstFile.Close() if err != nil { fmt.Println("目標文件關閉失敗,緣由是:", err) } }() if err != nil { fmt.Println("目標文件打開失敗,緣由是:", err) return 0, err } return io.Copy(dstFile, srcFile) } func main() { result, err := CopyFile("src/dst.jpeg", "src/哈哈.jpeg") if err == nil { fmt.Println("拷貝成功!拷貝的字節數爲: ", result) } }
對於大文件,咱們還能夠採用下面的方式
package main import ( "io" "log" "os" ) func CopyFile(srcFileName string, dstFileName string) { //打開源文件 srcFile, err := os.Open(srcFileName) if err != nil { log.Fatalf("源文件讀取失敗,緣由是:%v\n", err) } defer func() { err = srcFile.Close() if err != nil { log.Fatalf("源文件關閉失敗,緣由是:%v\n", err) } }() //建立目標文件,稍後會向這個目標文件寫入拷貝內容 distFile, err := os.Create(dstFileName) if err != nil { log.Fatalf("目標文件建立失敗,緣由是:%v\n", err) } defer func() { err = distFile.Close() if err != nil { log.Fatalf("目標文件關閉失敗,緣由是:%v\n", err) } }() //定義指定長度的字節切片,每次最多讀取指定長度 var tmp = make([]byte, 1024*4) //循環讀取並寫入 for { n, err := srcFile.Read(tmp) n, _ = distFile.Write(tmp[:n]) if err != nil { if err == io.EOF {//讀到了文件末尾,而且寫入完畢,任務完成返回(關閉文件的操做由defer來完成) return } else { log.Fatalf("拷貝過程當中發生錯誤,錯誤緣由爲:%v\n", err) } } } } func main() { CopyFile("./worm.mp4", "./dist.mp4") }
package main //咱們讀寫的文件通常存放於目錄中.所以,有時須要指定到某一個目錄下,根據目錄存儲的情況 //再進行文件的特定操做.接下來咱們看看目錄的基本操做方法. import ( "fmt" "log" "os" ) //打開目錄 //打開目錄咱們也使用OpenFile函數,但要指定不一樣的參數來通知系統,要打開的是一個目錄文件. //func OpenFile(name string, flag int, perm FileMode) (file *File, err error) //參數1: name,表示要打開的目錄名稱.使用絕對路徑較多 //參數2: flag,表示打開文件的讀寫模式 //參數3: perm,表示打開權限.但對於目錄來講有所不一樣,一般傳os.ModeDir. //返回值:因爲是操做目錄,因此file是指向目錄的文件指針.err中保存錯誤信息 //讀目錄內容 //這與讀文件有所不一樣.目錄中存放的是文件名和子目錄名.因此使用Readdir函數 //func (f *File) Readdir(n int) (fi []FileInfo, err error) //若是n>0,Readdir函數會返回一個最多n個成員的切片。這時,若是Readdir返回一個空切片, //它會返回一個非nil的錯誤說明緣由。若是到達了目錄f的結尾,返回值err會是io.EOF。 // //若是n<=0,Readdir函數返回目錄中剩餘全部文件對象的FileInfo構成的切片。 //此時,若是Readdir調用成功(讀取全部內容直到結尾),它會返回該切片和nil的錯誤值。 //若是在到達結尾前遇到錯誤,會返回以前成功讀取的FileInfo構成的切片和該錯誤。 func main() { //不推薦,由於經過查看ioutil.ReadDir()函數可知,官方使用的是os.Open()函數打開的目錄 //file, err := os.OpenFile("./dir", os.O_RDWR, os.ModeDir) file, err := os.Open("./dir") if err != nil { log.Fatalf("文件打開失敗,緣由是:%v\n", err) } defer func() { err = file.Close() if err != nil { log.Fatalf("文件關閉失敗,緣由是:%v\n", err) } }() //Readdir方法返回一個FileInfo接口類型的切片和一個error類型的錯誤 infos, err := file.Readdir(-1) for _, info := range infos { fmt.Printf("%v, %v\n", info.Name(), info.IsDir()) } }
方法1:使用os包
package main import ( "fmt" "os" ) var dirNames = make([]string, 0, 50) var pathSeparator = string(os.PathSeparator) func traverseDir(filePath string) error { file, err := os.Open(filePath) if err != nil { return err } fileInfo, err := file.Readdir(0) if err != nil { return err } for _, value := range fileInfo { if value.IsDir() { dirNames = append(dirNames, value.Name()) err = traverseDir(filePath+pathSeparator+value.Name()) if err != nil { return err } } } return err } func main() { var filePath = "./dir" err := traverseDir(filePath) if err != nil { fmt.Println(err) } fmt.Println(dirNames) }
方法2:使用ioutil包
package main import ( "fmt" "io/ioutil" "os" ) var dirNames = make([]string, 0, 50) var pathSeparator = string(os.PathSeparator) func traverseDir(filePath string) error { fileInfos, err := ioutil.ReadDir(filePath) if err != nil { return err } for _, fileInfo :=range fileInfos { if fileInfo.IsDir() { dirNames = append(dirNames, fileInfo.Name()) err = traverseDir(filePath+pathSeparator+fileInfo.Name()) if err != nil { return err } } } return err } func main() { var filePath = "./dir" err := traverseDir(filePath) if err != nil { fmt.Println(err) } fmt.Println(dirNames) }
package main import ( "fmt" "io/ioutil" "os" "strings" ) var pathSeparator = string(os.PathSeparator) //重命名文件 func renameFileName(filePath string, old string, new string) error { files, err := ioutil.ReadDir(filePath) if err != nil { return err } for _, fileInfo := range files { if !fileInfo.IsDir() { err = os.Rename(filePath + pathSeparator + fileInfo.Name(), filePath + pathSeparator + strings.Replace(fileInfo.Name(), old, new, -1), ) if err != nil { return err } } } return err } func main() { var filePath = "./dir" err := renameFileName(filePath, "f", "kkk") if err != nil { fmt.Printf("錯誤: %v\n", err) } }
package main import ( "fmt" "os" ) func main() { //Mkdir使用指定的權限和名稱建立一個目錄。若是出錯,會返回*PathError底層類型的錯誤。 err := os.Mkdir("./foo", 0755) if os.IsExist(err) { fmt.Println("目錄已存在") return } //MkdirAll使用指定的權限和名稱建立一個目錄,包括任何須要的上級目錄,並返回nil,不然返回錯誤。 //權限位perm會應用在每個被本函數建立的目錄上。若是path指定了一個已經存在的目錄,MkdirAll不作任何操做並返回nil。 err = os.MkdirAll("./foo/bar", 0755) if err != nil { fmt.Printf("%v\n", err) return } }
package main import ( "fmt" "os" ) func main() { //Remove刪除name指定的文件或目錄。若是出錯,會返回*PathError底層類型的錯誤。 //該方法不能刪除非空目錄,若是想刪除目錄以及目錄下的全部文件,可使用RemoveAll err := os.Remove("./def") if os.IsNotExist(err) { fmt.Println("您要刪除的文件或目錄不存在") return } if err != nil { fmt.Println(err) } //RemoveAll刪除path指定的文件,或目錄及它包含的任何下級對象。 //它會嘗試刪除全部東西,除非遇到錯誤並返回。 //若是path指定的對象不存在,RemoveAll會返回nil而不返回錯誤。 err = os.RemoveAll("./def") if err != nil { fmt.Println(err) } }