文件的打開
文件的常見的兩種打開方式是基于os
包所提供的兩個函數:
func Open(name string) (*File,error)
func OpenFile(name string flag int perm FileMode) (*File,error)
相對于前者,OpenFile
可以提供更加細致的操作,而前者就是對后者的一個簡單封裝
我們首先來看第一種使用方法,我們只需要提供對應的文件名就可以了,代碼如下:
func main() {file, err := os.Open("test.txt")defer file.Close()if err != nil {fmt.Println("文件訪問異常")return}
}
文件的查找路徑默認為項目go.mod
文件所在的路徑,由于項目下并沒有該文件,所以自然會返回一個錯誤。
因為IO錯誤的類型有很多,所以有時后需要我們去手動的去判斷文件是否存在,而os
包也為此提供了方便函數,修改后的函數如下:
func main() {file, err := os.Open("test.txt")defer file.Close()if os.IsNotExist(err) {fmt.Println("file not found")} else if err != nil {fmt.Println("error")} else {fmt.Println("file found")}
}
事實上第一種函數讀取的文件僅僅只是只讀的,無法被修改,Open
函數內部實現
func Open(name string) (*File, error) {return OpenFile(name, O_RDONLY, 0)
}
通過OpenFile
函數可以控制更多細節,例如修改文件描述符和文件權限,關于文件描述符,os
包下提供了以下常量以供使用。
解釋const (// 只讀,只寫,讀寫 三種必須指定一個O_RDONLY int = syscall.O_RDONLY // 以只讀的模式打開文件O_WRONLY int = syscall.O_WRONLY // 以只寫的模式打開文件O_RDWR int = syscall.O_RDWR // 以讀寫的模式打開文件// 剩余的值用于控制行為O_APPEND int = syscall.O_APPEND // 當寫入文件時,將數據添加到文件末尾O_CREATE int = syscall.O_CREAT // 如果文件不存在則創建文件O_EXCL int = syscall.O_EXCL // 與O_CREATE一起使用, 文件必須不存在O_SYNC int = syscall.O_SYNC // 以同步IO的方式打開文件O_TRUNC int = syscall.O_TRUNC // 當打開的時候截斷可寫的文件
)
關于文件權限的則提供了以下常量。
解釋const (ModeDir = fs.ModeDir // d: 目錄ModeAppend = fs.ModeAppend // a: 只能添加ModeExclusive = fs.ModeExclusive // l: 專用ModeTemporary = fs.ModeTemporary // T: 臨時文件ModeSymlink = fs.ModeSymlink // L: 符號鏈接ModeDevice = fs.ModeDevice // D: 設備文件ModeNamedPipe = fs.ModeNamedPipe // p: 具名管道 (FIFO)ModeSocket = fs.ModeSocket // S: Unix 域套接字ModeSetuid = fs.ModeSetuid // u: setuidModeSetgid = fs.ModeSetgid // g: setgidModeCharDevice = fs.ModeCharDevice // c: Unix 字符設備, 前提是設置了 ModeDeviceModeSticky = fs.ModeSticky // t: 黏滯位ModeIrregular = fs.ModeIrregular // ?: 非常規文件// 類型位的掩碼. 對于常規文件而言,什么都不會設置.ModeType = fs.ModeTypeModePerm = fs.ModePerm // Unix 權限位, 0o777
)
我們下面可以實現一個以讀寫模式打開一個文件的代碼例子,權限為0666
,表示為所有人都可以對該文件進行讀寫,且不存在時會自動創建。
func main() {file, err := os.OpenFile("test.txt", os.O_CREATE|os.O_RDWR, 0666)if os.IsNotExist(err) {fmt.Println("文件不存在")} else if err != nil {fmt.Println("文件打開有異常")} else {fmt.Println("文件打開成功", file.Name())defer file.Close()}
}
文件的讀取
常見的文件讀取
當我們成功打開文件以后,我們就可以開始進行讀取操作了,對于讀取文件的操作,os.file
提供了以下幾個公開的方法
// 將文件讀進傳入的字節切片
func (f *File) Read(b []byte) (n int, err error) // 相較于第一種可以從指定偏移量讀取
func (f *File) ReadAt(b []byte, off int64) (n int, err error) func
大部分情況下第一種情況使用的較多,針對第一種方法,我們需要自己編寫邏輯來進行讀取時切片的動態擴容,代碼如下:
func ReadFile(f *os.File) ([]byte, error) {buffer := make([]byte, 0, 512)for {if len(buffer) == cap(buffer) {//擴容buffer = append(buffer, 0)[:len(buffer)]}//繼續讀取offerset, err := f.Read(buffer[len(buffer):cap(buffer)])buffer = buffer[:len(buffer)+offerset]// 發生錯誤時if err != nil {if errors.Is(err, io.EOF) {err = nil}return buffer, err}}
}
剩余邏輯為:
func main() {file, err := os.OpenFile("test.txt", os.O_CREATE|os.O_RDWR, 0666)if err != nil {fmt.Println("文件打開異常")} else {fmt.Println("文件打開成功", file.Name())}bytes, err := ReadFile(file)if err != nil {fmt.Println("文件讀取異常")} else {fmt.Println("文件讀取成功", bytes)}file.Close()
}
除此之外,我們還可以使用兩個方便函數來進行文件讀取,分別是os
包下的ReadFile
函數以及io
包下的ReadAll
函數,相對于前者而言,我們只需要提供文件路徑即可,而后者我們則需要提供一個io.Raeder
類型的實現。
os.ReadFile
函數形式
func ReadFile(name string)([]byte,error)
使用例子:
func main() {bytes, err := os.ReadFile("README.txt")if err != nil {fmt.Println(err)} else {fmt.Println(string(bytes))}
}
io.ReadAll
函數形式:
func ReadAll(r Reader) ([]byte,error)
示例:
func main() {file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)if err != nil {fmt.Println("文件訪問異常")} else {fmt.Println("文件打開成功", file.Name())bytes, err := io.ReadAll(file)if err != nil {fmt.Println(err)} else {fmt.Println(string(bytes))}file.Close()}
}
文件的寫入
os.File
結構體提供以下幾種方法來供我們寫入數據:
//寫入字節切片
func (f *file) Write(b []byte) (int,error)//寫入字符串
func (f *file) WriteString(s string) (int,error) 從指定位置開始寫,當以os.O_APPEND模式打開時,會返回錯誤
func (f *File) WriteAt(b []byte, off int64) (n int, err error)
如果我們要對文件寫入數據。我們需要以O_WRONLY
或O_RDWR
模式打開文件,否則無法寫入,接下來我們來看一個示例:
func main() {file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666)if err != nil {fmt.Println("文件訪問異常")} else {fmt.Println("文件打開成功", file.Name())for i := 0; i < 5; i++ {offset, err := file.WriteString("hello world!\n")if err != nil {fmt.Println(offset, err)}}fmt.Println(file.Close())}
}
向文件寫入字節切片也是類似的操作,就不再贅述。對于寫入文件的操作標準庫同樣提供了方便函數,分別是os.WriteFile
與io.WriteString
os.WriteFile
func WriteFile(name string, data []byte, perm FileMode) error
使用例子如下
func main() {err := os.WriteFile("README.txt", []byte("hello world!\n"), 0666)if err != nil {fmt.Println(err)}
}
此時文件內容如下
hello world!
io.WriteString
func WriteString(w Writer, s string) (n int, err error)
使用例子如下
func main() {file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666)if err != nil {fmt.Println("文件訪問異常")} else {fmt.Println("文件打開成功", file.Name())for i := 0; i < 5; i++ {offset, err := io.WriteString(file, "hello world!\n")if err != nil {fmt.Println(offset, err)}}fmt.Println(file.Close())}
}
文件的復制
對于復制文件而言,需要同時打開兩個文件,第一種方法是將原文件中的數據讀取出來,然后寫入目標文件中,代碼示例如下
解釋func main() {// 從原文件中讀取數據data, err := os.ReadFile("README.txt")if err != nil {fmt.Println(err)return}// 寫入目標文件err = os.WriteFile("README(1).txt", data, 0666)if err != nil {fmt.Println(err)} else {fmt.Println("復制成功")}
}
os.File.ReadFrom
另一種方法是使用os.File
提供的方法ReadFrom
,打開文件時,一個只讀,一個只寫。
func (f *File) ReadFrom(r io.Reader) (n int64, err error)
使用示例如下
解釋func main() {// 以只讀的方式打開原文件origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)if err != nil {fmt.Println(err)return}defer origin.Close()// 以只寫的方式打開副本文件target, err := os.OpenFile("README(1).txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)if err != nil {fmt.Println(err)return}defer target.Close()// 從原文件中讀取數據,然后寫入副本文件offset, err := target.ReadFrom(origin)if err != nil {fmt.Println(err)return}fmt.Println("文件復制成功", offset)
}
io.Copy
還有一種方法就是使用io.Copy
方便函數
func Copy(dst Writer, src Reader) (written int64, err error)
使用示例如下
解釋func main() {// 以只讀的方式打開原文件origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)if err != nil {fmt.Println(err)return}defer origin.Close()// 以只寫的方式打開副本文件target, err := os.OpenFile("README(1).txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)if err != nil {fmt.Println(err)return}defer target.Close()// 復制written, err := io.Copy(target, origin)if err != nil {fmt.Println(err)} else {fmt.Println(written)}
}
文件重命名
重命名也可以理解為移動文件,會用到os
包下的Rename
函數。
func Rename(oldpath, newpath string) error
示例如下
解釋func main() {err := os.Rename("README.txt", "readme.txt")if err != nil {fmt.Println(err)} else {fmt.Println("重命名成功")}
}
**注意:**重命名文件夾,移動文件夾同樣適用。
文件的刪除
刪除操作相較于其他操作要簡單的多,只會用到os
包下的兩個函數
解釋// 刪除單個文件或者空目錄,當目錄不為空時會返回錯誤
func Remove(name string) error// 刪除指定目錄的所有文件和目錄包括子目錄與子文件
func RemoveAll(path string) error
使用起來十分的簡單,下面是刪除目錄的例子
解釋func main() {// 刪除當前目錄下所有的文件與子目錄err := os.RemoveAll(".")if err != nil {fmt.Println(err)}else {fmt.Println("刪除成功")}
}
下面刪除單個文件的例子