Go:文件操作


一、打開文件和關閉文件

os包File結構體的兩個方法:

func Open(name string) (file *File, err error)

Open打開一個文件用於讀取。如果操作成功,返回的文件對象的方法可用於讀取數據;對應的文件描述符具有O_RDONLY模式。如果出錯,錯誤底層類型是*PathError。

func (f *File) Close() error

Close關閉文件f,使文件不能用於讀寫。它返回可能出現的錯誤。

import (
    "fmt"
    "os"
)

func main() {
    // 打開文件
    file, err := os.Open("e:/a.txt")
    if err != nil {
        fmt.Printf("打開文件出錯:%v\n", err)
    }
    fmt.Println(file) // &{0xc00006a780}
    // 關閉文件
    err = file.Close()
    if err != nil {
        fmt.Printf("關閉文件出錯:%v\n", err)
    }
}
View Code

二、讀文件

讀取文件內容並顯示在終端

方式一:(帶緩沖方式)

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    // 打開文件
    file, err := os.Open("e:/a.txt")
    if err != nil {
        fmt.Printf("打開文件出錯:%v\n", err)
    }
    // 及時關閉文件句柄
    defer file.Close()
    // bufio.NewReader(rd io.Reader) *Reader
    reader := bufio.NewReader(file)
    // 循環讀取文件的內容
    for {
        line, err := reader.ReadString('\n') // 讀到一個換行符就結束
        if err == io.EOF { // io.EOF表示文件的末尾
            break
        }
        // 輸出內容
        fmt.Print(line)
    }
}
View Code

方式二:一次性將整個文件讀入到內存中,這種方式適用於文件不大的情況。(使用 io/ioutil.ReadFile 方法)

func ReadFile(filename string) ([]byte, error)

ReadFile 從filename指定的文件中讀取數據並返回文件的內容。成功的調用返回的err為nil而非EOF。因為本函數定義為讀取整個文件,它不會將讀取返回的EOF視為應報告的錯誤。

import (
    "fmt"
    "io/ioutil"
)

func main() {
    // 使用 io/ioutil.ReadFile 方法一次性將文件讀取到內存中
    filePath := "e:/.txt"
    content, err := ioutil.ReadFile(filePath)
    if err != nil {
        // log.Fatal(err)
        fmt.Printf("讀取文件出錯:%v", err)
    }
    fmt.Printf("%v\n", content)
    fmt.Printf("%v\n", string(content))
}
View Code

三、寫文件

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

OpenFile是一個更一般性的文件打開函數,大多數調用者都應用Open或Create代替本函數。它會使用指定的選項(如O_RDONLY等)、指定的模式(如0666等)打開指定名稱的文件。如果操作成功,返回的文件對象可用於I/O。如果出錯,錯誤底層類型是*PathError。

  1. 參數二:文件打開模式(可以組合);
  2. 參數三:用來在linux、unix系統下進行權限控制的。

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   // 打開文件用於同步I/O
    O_TRUNC  int = syscall.O_TRUNC  // 如果可能,打開時清空文件
)

寫文件操作應用實例

示例一:

  1. 創建一個新文件,寫入3行:"Hello World"
  2. 打開一個存在的文件,將原來的內容覆蓋成新的內容,3行:"你好,世界"
  3. 打開一個存在的文件,在原來的內容基礎上,追加3行:"你好,Golang"
  4. 打開一個存在的文件,將原來的內容讀出顯示在終端,並且追加3行:"你好,World"
import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    filePath := "e:/a.txt" // 此文件事先不存在
    file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666) // O_CREATE 能創建文件
    if err != nil {
        fmt.Printf("打開文件出錯:%v", err)
        return
    }
    // 及時關閉文件句柄
    defer file.Close()
    // 准備寫入的內容
    str := "Hello World\r\n"
    // 寫入時,使用帶緩沖方式的 bufio.NewWriter(w io.Writer) *Writer
    writer := bufio.NewWriter(file)
    // 使用for循環寫入內容
    for i := 0; i < 3; i++ {
        _, err := writer.WriteString(str) // func (b *Writer) WriteString(s string) (int, error)
        if err != nil {
            fmt.Printf("文件寫入出錯:%s", err)
        }
    }
    // 因為 writer 是帶緩存的,所以需要 Flush 方法將緩沖中的數據真正寫入到文件中
    _ = writer.Flush()
}
1

2:在1中修改,將 O_CREATE 修改為 O_TRUNC 模式即可,表示:打開文件並清空內容

3:在2中修改,將 O_TRUNC 修改為 O_APPEND 模式即可,表示:打開文件並在最后追加內容

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    filePath := "e:/a.txt"
    file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666)
    if err != nil {
        fmt.Printf("打開文件出錯:%v", err)
        return
    }
    defer file.Close()

    // 先讀取原來文件的內容,並顯示在終端
    reader := bufio.NewReader(file)
    for {
        str, err := reader.ReadString('\n') // 讀到一個換行符就結束
        if err == io.EOF { // io.EOF表示文件的末尾
            break
        }
        // 輸出內容
        fmt.Print(str)
    }

    // 准備寫入的內容
    str := "你好,World\r\n"
    // 寫入時,使用帶緩沖方式的 bufio.NewWriter(w io.Writer) *Writer
    writer := bufio.NewWriter(file)
    // 使用for循環寫入內容
    for i := 0; i < 3; i++ {
        _, err := writer.WriteString(str) // func (b *Writer) WriteString(s string) (int, error)
        if err != nil {
            fmt.Printf("文件寫入出錯:%s", err)
        }
    }
    // 因為 writer 是帶緩存的,所以需要 Flush 方法將緩沖中的數據真正寫入到文件中
    _ = writer.Flush()
}
4:讀寫模式(O_RDWR)

示例二:將一個文件內容,寫入到另一個文件中。(這兩個文件已存在)

這里使用 io/ioutil 的兩個方法,ReadFile 和 WriteFile:

import (
    "fmt"
    "io/ioutil"
)

func main() {
    filePath1 := "e:/a.txt"
    filePath2 := "e:/b.txt"
    content, err := ioutil.ReadFile(filePath1)
    if err != nil {
        fmt.Printf("讀取文件出錯:%v", err)
        return
    }
    err = ioutil.WriteFile(filePath2, content, 0666)
    if err != nil {
        fmt.Printf("寫入文件出錯:%v", err)
        return
    }
}
View Code

四、判斷文件是否存在

golang判斷文件或文件夾是否存在的方法為使用 os.Stat() 函數返回的錯誤值進行判斷:

  1. 如果返回的錯誤為 nil,說明文件或文件夾存在;
  2. 如果返回的錯誤類型使用 os.IsNotExist() 判斷為 true,說明文件或文件夾不存在;
  3. 如果返回的錯誤為其他類型,則不確定是否存在。
package main

import (
    "fmt"
    "os"
)

// 判斷文件或文件夾是否存在
func PathExist(path string) (bool, error) {
    _, err := os.Stat(path)
    if err == nil {
        return true, nil
    }
    if os.IsNotExist(err) {
        return false, nil
    }
    return false, err
}

func main() {
    filePath := "e:/a.txt"
    flag, err := PathExist(filePath)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(flag) // true
}
自己寫一個函數判斷

五、拷貝文件(帶緩沖方式)

使用 io/Copy 函數:

func Copy(dst Writer, src Reader) (written int64, err error)

將src的數據拷貝到dst,直到在src上到達EOF或發生錯誤。返回拷貝的字節數和遇到的第一個錯誤。

對成功的調用,返回值err為nil而非EOF,因為Copy定義為從src讀取直到EOF,它不會將讀取到EOF視為應報告的錯誤。如果src實現了WriterTo接口,本函數會調用src.WriteTo(dst)進行拷貝;否則如果dst實現了ReaderFrom接口,本函數會調用dst.ReadFrom(src)進行拷貝。

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

// 將 srcFilePath 拷貝到 dstFilePath
func CopyFile(dstFilePath string, srcFilePath string) (written int64, err error) {
    // 打開srcFilePath
    srcFile, err := os.Open(srcFilePath)
    if err != nil {
        fmt.Printf("打開文件出錯:%s\n", err)
        return
    }
    defer srcFile.Close()
    // 通過 bufio/NewReader,傳入 srcFile,獲取到 reader
    reader := bufio.NewReader(srcFile)
    // 打開dstFilePath
    dstFile, err := os.OpenFile(dstFilePath, os.O_WRONLY | os.O_CREATE, 0666)
    if err != nil {
        fmt.Printf("打開文件出錯:%s\n", err)
        return
    }
    defer dstFile.Close()
    // 通過 bufio/NewWriter,傳入 dstFile,獲取到 writer
    writer := bufio.NewWriter(dstFile)
    return io.Copy(writer, reader)
}

func main() {
    srcFilePath := "e:/a.mp4"
    dstFilePath := "f:/b.mp4"
    _, err := CopyFile(dstFilePath, srcFilePath)
    if err != nil {
        fmt.Printf("拷貝文件出錯:%s", err)
    }
    fmt.Println("拷貝文件完成")
}
自己寫一個函數完成拷貝文件

六、遍歷一個目錄

package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Println("請輸入一個目錄的路徑:")
    var path string
    _, _ = fmt.Scan(&path)
    // 打開目錄
    f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)
    if err != nil {
        fmt.Printf("Open file failed:%s.\n", err)
        return
    }
    defer f.Close()
    // 讀取目錄
    info, err := f.Readdir(-1) // -1 表示讀取目錄中所有目錄項
    // 遍歷返回的切片
    for _, fileInfo := range info {
        if fileInfo.IsDir() {
            fmt.Printf("%s是一個目錄\n", fileInfo.Name())
        } else {
            fmt.Printf("%s是一個文件\n", fileInfo.Name())
        }
    }
}
View Code

七、其他

統計一個文件中含有的英文、數字、空格以及其他字符數量。

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

// 定義一個結構體,用於保存統計結果
type CharCount struct {
    AlphaCount     int // 記錄英文個數
    NumCount     int // 記錄數字的個數
    SpaceCount     int // 記錄空格的個數
    OtherCount     int // 記錄其它字符的個數
}

func main() {
    // 思路: 打開一個文件, 創一個 reader
    // 每讀取一行,就去統計該行有多少個 英文、數字、空格和其他字符
    // 然后將結果保存到一個結構體
    filePath := "e:/a.txt"
    file, err := os.Open(filePath)
    if err != nil {
        fmt.Printf("打開文件出錯:%s\n", err)
        return
    }
    defer file.Close()
    // 定義一個 CharCount 實例
    var count CharCount
    //創建一個Reader
    reader := bufio.NewReader(file)
    // 開始循環的讀取文件的內容
    for {
        line, err := reader.ReadString('\n')
        if err == io.EOF { // 讀到文件末尾就退出
            break
        }
        // 遍歷每一行(line),進行統計
        for _, v := range line {
            switch {
                case v >= 'a' && v <= 'z':
                    fallthrough // 穿透
                case v >= 'A' && v <= 'Z':
                    count.AlphaCount++
                case v >= '0' && v <= '9':
                    count.NumCount++
                case v == ' ' || v == '\t':
                    count.SpaceCount++
                default :
                    count.OtherCount++
            }
        }
    }
    // 輸出統計的結果看看是否正確
    fmt.Printf("字符的個數為:%v\n數字的個數為:%v\n空格的個數為:%v\n其它字符個數:%v\n",
        count.AlphaCount, count.NumCount, count.SpaceCount, count.OtherCount)
}
View Code

 


免責聲明!

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



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