Go文件操作

目錄

  • 示例1: 打開和關閉文件
  • 示例2: 打開文件並讀取內容
  • 示例3: 一次性讀取文件
  • 示例4: 帶緩衝的Reader讀文件
  • 示例5: 創建文件並寫入內容
  • 示例6: 寫文件的四種方式
  • 示例7: 把一個文件內容寫入到另一個文件
  • 示例8:使用bufio獲取用戶輸入
  • 示例9: 判斷文件或目錄是否存在
  • 示例10: 拷貝文件、圖片音視頻
  • 示例11: 遍歷目錄
    • 遍歷目錄
    • 僅遍歷目錄,忽略文件
  • 示例12: 修改文件名
  • 示例13:創建目錄
  • 示例14:刪除文件

對於文件,我們並不陌生,文件是數據源(保存數據的地方)的一種,比如大家經常使用的word文檔,txt文件,Excel文件…等等都是文件。文件最主要的作用就是保存數據,它既可以保存一張圖片,也可以保存視頻,聲音……
文件在程序中是以流的形式來操作的。

:數據在數據源(文件)和程序(內存)之間經歷的路徑
輸出流:數據從程序(內存)到數據源(文件)的路徑
輸入流:數據從數據源(文件)到程序(內存)的路徑
輸入與輸出都是相對於內存而言的,從內存向外流就是輸出,從外部向內存流就是輸入

在Go中,我們操作文件的方法在os包中,會經常使用到os.File結構體 Go語言標準庫文檔

示例1: 打開和關閉文件

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)
        }
    }()
}

示例2: 打開文件並讀取內容

使用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))
}

示例3: 一次性讀取文件

讀取文件內容並显示在終端,將文件內容一次性讀取到終端,適用於文件不大的情況。

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))
}

示例4: 帶緩衝的Reader讀文件

讀取文件的內容並显示在終端(帶緩衝區的方式),使用os.Open, file.Close,bufio.NewReader,reader.ReadString函數和方法。適合讀取大文件
1.使用ReadBytes方法
代碼1:

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方法

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)
}

示例5: 創建文件並寫入內容

第二個參數:文件代開模式(可以組合);第三個參數:權限控制(如0755)

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)
	}
}

示例6: 寫文件的四種方式

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()
}

示例7: 把一個文件內容寫入到另一個文件

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)
    }
}

示例8:使用bufio獲取用戶輸入

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)
}

示例9: 判斷文件或目錄是否存在

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
}

示例10: 拷貝文件、圖片音視頻

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")
}

示例11: 遍歷目錄

遍歷目錄

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)
}

示例12: 修改文件名

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)
    }
}

示例13:創建目錄

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
	}
}

示例14:刪除文件

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)
	}
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

您可能也會喜歡…