Go 檔案操作
檔案操作
作為後端語言,Go
通過os
包提供了對檔案的操作。
同時,使用bufio
與ioutil
也可以進行檔案操作,三者均有自身的優劣勢結合不同的需求使用不同的包來進行操作,將會讓你的事半功倍。
檔案分為普通檔案和二進位制檔案,使用二進位制檔案時應該按照byte
進行讀取。
OpenFile
在os
包中,有一個方法名為OpenFile()
,它可以用指定模式來開啟一個檔案物件,並且會返回一個檔案控制代碼。
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
...
}
name:檔案路徑
flag:開啟檔案的模式
perm:許可權,一個八進位制數。r(讀)04,w(寫)02,x(執行)01。
下表是flag
所支援的模式選項,如果要新增多種模式,使用|
進行分割。
模式 | 含義 |
---|---|
os.O_WRONLY | 只寫 |
os.O_CREATE | 建立檔案 |
os.O_RDONLY | 只讀 |
os.O_RDWR | 讀寫 |
os.O_TRUNC | 清空 |
os.O_APPEND | 追加 |
讀取檔案
內容準備
下面有一個test.txt
檔案,裡面儲存了一些歌曲名稱,專輯名稱,作者名稱等資訊。
海闊天空 | Words & Music Final Live | Beyond 紅豆 | 唱遊 | 王菲 我可以抱你嗎 | 重拾女人心 | 張惠妹 味道 | 味道 | 辛曉琪 獨角戲 | 如果雲知道 | 許茹芸
開啟檔案時,可以使用os.Open()
方法進行開啟,它其實是基於os.OpenFile()
的一個封裝。
預設是以只讀的方式進行開啟。返回一個檔案控制代碼與錯誤物件。
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
file.Read
當獲取到檔案控制代碼後,可使用Read()
方法對其進行讀取。
func (f *File) Read(b []byte) (n int, err error) { if err := f.checkValid("read"); err != nil { return 0, err } n, e := f.read(b) return n, f.wrapErr("read", e) }
這是一個檔案控制代碼的方法,接收一個byte
的切片,返回一個int
與error
,其中int
是已讀取的位元組數。
以下是基本使用:
package main
import (
"fmt"
"io"
"os"
)
func readTxt() {
// 開啟檔案,只讀方式
file, err := os.Open("./file.txt")
if err != nil {
fmt.Println("開啟檔案出錯:", err)
return
}
defer file.Close() // 關閉檔案
var content []byte
var temp = make([]byte, 128) // 建立容納讀取檔案的一個變數,byte()型別
for {
// n 以讀取的位元組數
n, err := file.Read(temp) // 使用檔案控制代碼,開始讀取。 讀取的內容放入temp變數中
if err == io.EOF { // 丟擲EOF錯誤代表檔案讀取完畢
fmt.Println("檔案讀取完畢")
break
}
if err != nil {
fmt.Println("讀取檔案出錯:", err)
return
}
content = append(content, temp[:n]...) // 使用展開語法,將其內容進行展開
}
fmt.Println(string(content)) // []byte轉換為string
}
func main() {
readTxt()
}
bufio
bufio
是一個第三方的包,基於file
做了一層封裝,支援更多的功能。
並且它具有緩衝區,能夠讓讀寫更加迅速。
使用NewReader()
方法,放入檔案控制代碼,返回一個讀取物件,然後再進行指定的方法讀取該物件。
提供的讀取方法如下:
方法 | 描述 |
---|---|
Read | 接收一個byte切片,返回以讀取位元組數 |
ReadSlice | 返回一個切片,byte型別 |
ReadByte | 返回byte字串 |
ReadBytes | 返回一個切片,byte型別 |
ReadRune | 返回一個rune字串 |
ReadString | 返回一個string的字串 |
ReadLine | 以行進行讀取,返回一個byte切片,並且會返回一個布林值判斷是否讀取完畢 |
其實用ReadString
就能滿足大部分需求了。
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func readTxt() {
// 開啟檔案,只讀方式
file, err := os.Open("./file.txt")
if err != nil {
fmt.Println("開啟檔案出錯:", err)
return
}
defer file.Close() // 關閉檔案
var content string
reader := bufio.NewReader(file) // 放入檔案控制代碼,返回讀取物件
for {
line, err := reader.ReadString('\n') // 以字元 \n 作為分割,即每次讀取一行
if err == io.EOF {
fmt.Println("檔案讀取完畢")
break
}
if err != nil {
fmt.Println("讀取檔案時出錯")
return
}
content += line
}
fmt.Println(content) // 拼接內容
}
func main() {
readTxt()
}
ioutil
該包位於os/ioutil
中,使用其ReadFile()
可快速的讀取完整個檔案。
package main
import (
"fmt"
"io/ioutil"
)
func readTxt() {
content, err := ioutil.ReadFile("./file.txt")
if err != nil {
fmt.Println("讀取檔案錯誤:", err)
return
}
fmt.Println(string(content))
}
func main() {
readTxt()
}
寫入檔案
write&writeString
write()
接收一個byte
切片,返回一個以讀取位元組數的int
和error
。
writeString()
則接收一個string
字串,返回的內容同上。
package main
import (
"fmt"
"os"
)
func writeTxt() {
// 建立檔案,只寫
file, err := os.OpenFile("newFile.txt", os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("開啟檔案時出錯", err)
return
}
defer file.Close()
// 寫入檔案
str := "第一行內容\n"
file.Write([]byte(str)) // 寫入位元組切片
file.WriteString("第二行內容\n")
}
func main() {
writeTxt()
}
bufio.NewWriter
bufio
具有緩衝區,所以讀寫更加迅速。
下面是基本使用,其實關於寫入物件的寫入檔案的方法還有很多,這裡不再一一例舉。
package main
import (
"bufio"
"fmt"
"os"
)
func writeTxt() {
// 建立檔案,只寫,清空
file, err := os.OpenFile("newFile.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
fmt.Println("開啟檔案時出錯", err)
return
}
defer file.Close()
// 寫入檔案
writer := bufio.NewWriter(file) // 放入檔案控制代碼,獲得一個寫入物件
writer.WriteString("一行新資料")
writer.Flush() // 快取重新整理到磁碟
}
func main() {
writeTxt()
}
ioutil.WriteFile
該方法接收一個byte
的切片,我們可以直接放入byte
切片然後進行寫入。
package main
import (
"fmt"
"io/ioutil"
)
func writeTxt() {
str := "新資料哦"
// 直接寫入,清空之前的,會建立新檔案
err := ioutil.WriteFile("./newFile.txt", []byte(str), 0666)
if err != nil {
fmt.Println("write file failed, err:", err)
return
}
}
func main() {
writeTxt()
}
指標偏移
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
Seeker介面用於包裝基本的移位方法。
Seek方法設定下一次讀寫的位置:偏移量為offset,校準點由whence確定:0表示相對於檔案起始;1表示相對於當前位置;2表示相對於檔案結尾。Seek方法返回新的位置以及可能遇到的錯誤。
移動到一個絕對偏移量為負數的位置會導致錯誤。移動到任何偏移量為正數的位置都是合法的,但其下一次I/O操作的具體行為則要看底層的實現。
以下是一個操縱示例,將最後一行內容進行覆蓋修改。
海闊天空 | Words & Music Final Live | Beyond
紅豆 | 唱遊 | 王菲
我可以抱你嗎 | 重拾女人心 | 張惠妹
味道 | 味道 | 辛曉琪
獨角戲 | 如果雲知道 | 許茹芸
操縱過程:
package main
import (
"fmt"
"os"
)
func writeTxt() {
// 建立檔案,只寫
file, err := os.OpenFile("file.txt", os.O_WRONLY, 0666)
if err != nil {
fmt.Println("開啟檔案時出錯", err)
return
}
defer file.Close()
// 設定指標移動
file.Seek(-40, 2)
// 寫入檔案
str := "全新歌曲 | 全新專輯 | 新歌手\n"
file.Write([]byte(str)) // 寫入位元組切片
}
func main() {
writeTxt()
}
最後結果:
海闊天空 | Words & Music Final Live | Beyond
紅豆 | 唱遊 | 王菲
我可以抱你嗎 | 重拾女人心 | 張惠妹
味道 | 味道 | 辛曉琪
全新歌曲 | 全新專輯 | 新歌手 // 進行覆蓋
拷貝檔案
藉助io.Copy()
實現一個拷貝檔案函式。
// CopyFile 拷貝檔案函式
func CopyFile(dstName, srcName string) (written int64, err error) {
// 以讀方式開啟原始檔
src, err := os.Open(srcName)
if err != nil {
fmt.Printf("open %s failed, err:%v.\n", srcName, err)
return
}
defer src.Close()
// 以寫|建立的方式開啟目標檔案
dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
fmt.Printf("open %s failed, err:%v.\n", dstName, err)
return
}
defer dst.Close()
return io.Copy(dst, src) //呼叫io.Copy()拷貝內容
}
func main() {
_, err := CopyFile("dst.txt", "src.txt")
if err != nil {
fmt.Println("copy file failed, err:", err)
return
}
fmt.Println("copy done!")
}