Golang: 常用的文件讀寫操作


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

以上就是常用的文件讀寫方式,今天就總結到這里吧,后續有機會再探討更多內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM