Go 語言提供了很多文件操作的支持,在不同場景下,有對應的處理方式,今天就來系統地梳理一下,幾種常用的文件讀寫的形式。
一、讀取文件內容
1、按字節讀取文件
這種方式是以字節為單位來讀取,相對底層一些,代碼量也較大,我們看下面代碼:
// read-bytes.go
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
// 字節切片緩存 存放每次讀取的字節
buf := make([]byte, 1024)
// 該字節切片用於存放文件所有字節
var bytes []byte
for {
// 返回本次讀取的字節數
count, err := file.Read(buf)
// 檢測是否到了文件末尾
if err == io.EOF {
break;
}
// 取出本次讀取的數據
currBytes := buf[:count]
// 將讀取到的數據 追加到字節切片中
bytes = append(bytes, currBytes...)
}
// 將字節切片轉為字符串 最后打印出來文件內容
fmt.Println(string(bytes))
}
2、結合 ioutil 來讀取
如果我們不想那么麻煩,可以結合 ioutil
來精簡上面的代碼:
// read-with-util.go
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
// ReadAll接收一個io.Reader的參數 返回字節切片
bytes, _ := ioutil.ReadAll(file)
fmt.Println(string(bytes))
}
由於 os.File 也是 io.Reader 的實現,我們可以調用 ioutil.ReadAll(io.Reader)
方法,將文件所有字節讀取出來,省去了使用字節緩存循環讀取的過程。
3、僅使用 ioutil 包來完成讀取操作:
為了進一步簡化文件讀取操作,ioutil 還提供了 ioutil.ReadFile(filename string)
方法,一行代碼搞定讀取任務:
// read-by-util.go
package main
import (
"fmt"
"io/ioutil"
)
func main() {
bytes, _ := ioutil.ReadFile("test.txt")
fmt.Println(string(bytes))
}
4、逐行讀取:
有時候為了便於分析處理,我們希望能夠逐行讀取文件內容,這個時候可以 Scanner 來完成:
// read-line-by-line.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
// 接受io.Reader類型參數 返回一個bufio.Scanner實例
scanner := bufio.NewScanner(file)
var count int
for scanner.Scan() {
count++
// 讀取當前行內容
line := scanner.Text()
fmt.Printf("%d %s\n", count, line)
}
}
這簡直就是 java.util.Scanner 翻版嘛,並且 Go 語言中的 Scanner 也可以接收不同的輸入源,比如 os.Stdin 等。
上面代碼直接打印出了每一行的數據,如果大家想得到最終文件的內容,可以創建一個字符串切片,每次逐行掃描時,將當前行內容追加到切片中即可。
以上就是幾種常用的文件讀取方式,當然還有其他更高級的方式,有機會再做總結。
二、寫入文件操作
1、使用 ioutil 完成寫入操作
上面我們介紹了 ioutil.ReadFile(filename string),與之對應地有 ioutil.WriteFile(filename string, ...)
方法,可以輕松完成寫入操作:
// write-by-util.go
package main
import (
"io/ioutil"
)
func main() {
data := []byte("hello goo\n")
// 覆蓋式寫入
ioutil.WriteFile("test.txt", data, 0664)
}
我們看到,WriteFile() 方法需要傳入三個參數,它的完整簽名是:ioutil.WriteFile(filename string, data []byte, perm os.FileMode)。如果文件不存在,則會根據指定的權限創建文件,如果存在,則會先清空文件原有內容,然后再寫入新數據。
需要注意最后一個參數,它是一個無符號 32 位整數,表示當前文件的權限,也是標准的 Unix 文件權限格式。
Unix 使用 -rwxrwxrwx
這樣的形式來表示文件權限,其中:
- 第1位:文件屬性,
-
表示是普通文件,d
表示是一個目錄 - 第2-4位:文件所有者的權限
- 第5-7位:文件所屬用戶組的權限
- 第8-10位:其他人的權限
在權限設置中:
r
表示 read,值為4
w
表示 write,值為2
x
表示 exec,值為1
下面我們通過 os.FileMode
測試一下:
package main
import (
"fmt"
"os"
)
func showMode(code int) {
fmt.Println(os.FileMode(code).String())
}
func main() {
showMode(0777)
showMode(0766)
showMode(0764)
}
運行程序,控制台打印如下:
-rwxrwxrwx
-rwxrw-rw-
-rwxrw-r--
2、通過File句柄完成寫入操作
上面我們曾使用過 os.Open(name string) 方法,這個方法是以只讀方式打開文件的,os 包還提供了 os.OpenFile(name string, flag int, perm FileMode) 方法,通過指定額外的 讀寫方式
和 文件權限
參數,使文件操作變得更為靈活。
其中,flag 有以下幾種常用的值:
- os.O_CREATE: create if none exists 不存在則創建
- os.O_RDONLY: read-only 只讀
- os.O_WRONLY: write-only 只寫
- os.O_RDWR: read-write 可讀可寫
- os.O_TRUNC: truncate when opened 文件長度截為0:即清空文件
- os.O_APPEND: append 追加新數據到文件
在打開文件之后,我們可以通過 Write() 和 WriteString() 方法寫入數據,最后通過 Sync() 方法將數據持久化到磁盤:
// write-by-file-descriptor.go
package main
import (
"fmt"
"os"
)
// 打印寫入的字節數
func printWroteBytes(count int) {
fmt.Printf("wrote %d bytes\n", count)
}
func main() {
// 以指定的權限打開文件
file, _ := os.OpenFile("test2.txt", os.O_RDWR | os.O_APPEND | os.O_CREATE, 0664)
defer file.Close()
data := []byte("hello go\n")
// 寫入字節
count, _ := file.Write(data)
printWroteBytes(count)
// 寫入字符串
count, _ = file.WriteString("hello world\n")
printWroteBytes(count)
// 確保寫入到磁盤
file.Sync()
}
3、通過bufio包完成寫入操作
這種方式其實是在 File 句柄上做了一層封裝,調用方式和上面直接寫入非常相似,大家僅做個參考:
// write-with-bufio.go
package main
import (
"bufio"
"fmt"
"os"
)
func printWroteBytes(count int) {
fmt.Printf("wrote %d bytes\n", count)
}
func main() {
file, _ := os.OpenFile("test.txt", os.O_RDWR | os.O_APPEND | os.O_CREATE, 0664)
defer file.Close()
// 獲取bufio.Writer實例
writer := bufio.NewWriter(file)
// 寫入字符串
count, _ := writer.Write([]byte("hello go\n"))
fmt.Printf("wrote %d bytes\n", count)
// 寫入字符串
count, _ = writer.WriteString("hello world\n")
fmt.Printf("wrote %d bytes\n", count)
// 清空緩存 確保寫入磁盤
writer.Flush()
}
以上就是常用的文件讀寫方式,今天就總結到這里吧,后續有機會再探討更多內容。