1. 程式人生 > >golang教程之讀取檔案

golang教程之讀取檔案

文章目錄

讀取檔案

原文:https://golangbot.com/read-files/

在這裡插入圖片描述

將整個檔案讀入記憶體

最基本的檔案操作之一是將整個檔案讀入記憶體。 這是在ioutil包的ReadFile函式的幫助下完成的。

讓我們從go程式所在的目錄中讀取一個檔案。 我已經在我的GOROOT

內部建立了一個資料夾filehandling ,我在裡面有一個文字檔案test.txt,它將使用我們的Go程式filehandling.go來讀取。 test.txt包含文字“Hello World,歡迎使用Go中的檔案處理。”。 這是我的資料夾結構。

src  
    filehandling
                filehandling.go
                test.txt

讓我們馬上看看程式碼吧。

package main

import (  
    "fmt"
    "io/ioutil"
)

func main() {  
    data,
err := ioutil.ReadFile("test.txt") if err != nil { fmt.Println("File reading error", err) return } fmt.Println("Contents of file:", string(data)) }

請從本地環境執行此程式,因為無法在playground上讀取檔案。

上述程式第9行中讀取檔案並返回儲存在資料中的位元組片。我們將資料轉換為字串並顯示檔案的內容。

請從test.txt所在的位置執行該程式。

例如,在linux/mac

的情況下,如果test.txt位於home/naveen/go/src/filehandling,則使用以下步驟執行該程式,

$]cd /home/naveen/go/src/filehandling/
$]go install filehandling
$]workspacepath/bin/filehandling

對於windows,如果test.txt位於C:\Users\naveen.r\go\src\filehandling,則使用以下步驟執行此程式,

Contents of file: Hello World. Welcome to file handling in Go.  

如果從任何其他位置執行此程式,例如嘗試從/home/userdirectory執行該程式,它將列印以下錯誤。

File reading error open test.txt: The system cannot find the file specified.  

原因是Go是一種編譯語言。 安裝的作用是,它從原始碼建立二進位制檔案。 二進位制檔案獨立於原始碼,可以從任何位置執行。 由於在執行二進位制檔案的位置找不到test.txt,程式會報錯它無法找到指定的檔案。

有三種方法可以解決這個問題,

  • 使用絕對檔案路徑
  • 將檔案路徑作為命令列標誌傳遞
  • 將文字檔案與二進位制檔案捆綁在一起

讓我們一個一個討論。

1.使用絕對檔案路徑

解決此問題的最簡單方法是傳遞絕對檔案路徑。 我修改了程式並將路徑更改為絕對路徑。

package main

import (  
    "fmt"
    "io/ioutil"
)

func main() {  
    data, err := ioutil.ReadFile("/home/naveen/go/src/filehandling/test.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

現在程式可以從任何位置執行,它將列印test.txt的內容。

例如,即使我從我的主目錄執行它也會工作

$]cd $HOME
$]go install filehandling
$]workspacepath/bin/filehandling

該程式將列印test.txt的內容

這似乎是一種簡單的方法,但隨之而來的是檔案應位於程式中指定的路徑中的缺陷,否則此方法將失敗。

2.將檔案路徑作為命令列標誌傳遞

解決此問題的另一種方法是將檔案路徑作為命令列標誌傳遞。 使用flag包,我們可以從命令列獲取檔案路徑作為輸入,然後讀取其內容。

讓我們首先了解flag包的工作原理。 flag包具有String函式。 此函式接受3個引數。 第一個是標誌的名稱,第二個是預設值,第三個是標誌的簡短描述。

讓我們編寫一個小程式來從命令列中讀取檔名。 用以下內容替換filehandling.go的內容,

package main  
import (  
    "flag"
    "fmt"
)

func main() {  
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()
    fmt.Println("value of fpath is", *fptr)
}

在上面的程式第8行中,建立一個名為fpath的字串標誌,其預設值為test.txt,並使用String函式讀取描述檔案路徑。 此函式返回儲存標誌值的字串變數的地址。

應該在程式訪問任何標誌之前呼叫flag.Parse()

我們在第一行列印標誌的值。

使用該命令執行此程式時

wrkspacepath/bin/filehandling -fpath=/path-of-file/test.txt

我們將/path-of-file/test.txt作為標誌fpath的值傳遞。

該程式輸出

value of fpath is /path-of-file/test.txt  

如果程式僅使用檔案處理執行而不傳遞任何fpath,它將列印

value of fpath is test.txt  

因為test.txt是fpath的預設值。

現在我們知道如何從命令列讀取檔案路徑,讓我們繼續完成我們的檔案讀取程式。

package main  
import (  
    "flag"
    "fmt"
    "io/ioutil"
)

func main() {  
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()
    data, err := ioutil.ReadFile(*fptr)
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

上面的程式讀取從命令列傳遞的檔案路徑的內容。 使用該命令執行此程式

wrkspacepath/bin/filehandling -fpath=/path-of-file/test.txt  

請將/path-of-file/替換為test.txt的實際路徑。 該程式將列印

Contents of file: Hello World. Welcome to file handling in Go.  

3.將文字檔案與二進位制檔案捆綁在一起

從命令列獲取檔案路徑的上述選項很好,但有一種更好的方法可以解決這個問題。 如果我們能夠將文字檔案與二進位制檔案捆綁在一起,那會不會很棒。 這就是我們接下來要做的事情。

有各種軟體包可以幫助我們實現這一目標。 我們將使用packr,因為它非常簡單,我一直在使用它在我的專案裡沒有任何問題。

第一步是安裝packr包。

在命令提示符中鍵入以下命令以安裝該程式包

go get -u github.com/gobuffalo/packr/...  

packr將靜態檔案(如.txt)轉換為.go檔案,然後直接嵌入到二進位制檔案中。 Packer非常智慧,可以在開發過程中從磁碟而不是從二進位制檔案中獲取靜態檔案。 這樣可以防止在只有靜態檔案發生變化時需要在開發期間重新編譯。

一個程式將使我們更好地理解事物。 用以下內容替換filehandling.go的內容,

package main

import (  
    "fmt"

    "github.com/gobuffalo/packr"
)

func main() {  
    box := packr.NewBox("../filehandling")
    data := box.String("test.txt")
    fmt.Println("Contents of file:", data)
}

在上面的第10行程式,我們正在建立一個新盒子。盒子表示一個資料夾,其內容將嵌入到二進位制檔案中。 在這種情況下,我指定包含test.txtfilehandling資料夾。 在下一行中,我們讀取檔案的內容並打印出來。

當我們處於開發階段時,我們可以使用go install命令來執行該程式。 它將按預期工作。 packr足夠智慧,可以在開發階段從磁碟載入檔案。

使用以下命令執行該程式。

go install filehandling  
workspacepath/bin/filehandling  

這些命令可以從任何位置執行。 Packr足夠智慧,可以獲取傳遞給NewBox命令的目錄的絕對路徑。

這個程式將打印出來

Contents of file: Hello World. Welcome to file handling in Go.

嘗試更改test.txt的內容並再次執行檔案處理。 您可以看到該程式列印test.txt的更新內容,而無需任何重新編譯。完美。

現在讓我們將test.txt捆綁到我們的二進位制檔案中。 我們使用packr命令來執行此操作。

執行以下命令

packr install -v filehandling 

這將打印出來

building box ../filehandling  
packing file filehandling.go  
packed file filehandling.go  
packing file test.txt  
packed file test.txt  
built box ../filehandling with ["filehandling.go" "test.txt"]  
filehandling  

此命令將靜態檔案與二進位制檔案捆綁在一起。

執行上述命令後,使用命令workspacepath/bin/filehandling執行程式。 該程式將列印test.txt的內容。 現在正在從二進位制檔案中讀取test.txt

如果您懷疑檔案是從二進位制檔案還是從磁碟提供,我建議您刪除test.txt並再次執行命令檔案控制代碼。 您可以看到test.txt的內容已打印出來。 太棒了,我們已成功將靜態檔案嵌入到二進位制檔案中。

以小塊讀取檔案

在上一節中,我們學習瞭如何將整個檔案載入到記憶體中。 當檔案的大小非常大時,將整個檔案讀入記憶體是沒有意義的,尤其是在RAM不足的情況下。 更優化的方法是以小塊讀取檔案。 這可以在bufio包的幫助下完成。

讓我們編寫一個程式,以3個位元組的塊為單位讀取test.txt檔案。 用以下內容替換filehandling.go的內容,

package main

import (  
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {  
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()

    f, err := os.Open(*fptr)
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err = f.Close(); err != nil {
            log.Fatal(err)
        }
    }()
    r := bufio.NewReader(f)
    b := make([]byte, 3)
    for {
        _, err := r.Read(b)
        if err != nil {
            fmt.Println("Error reading file:", err)
            break
        }
        fmt.Println(string(b))
    }
}

在上面的程式中,我們使用從命令列標誌傳遞的路徑開啟檔案。

在第19行我們延遲關閉檔案。

上面的程式第24行中建立了一個新的緩衝讀取器。 在下一行中,我們建立一個長度和容量為3的位元組片,檔案的位元組將被讀入其中。

第27行中的Read方法讀取len(b)位元組,即最多3個位元組,並返回讀取的位元組數。 一旦到達檔案末尾,它將返回EOF錯誤。

如果我們使用命令執行上面的程式,

$]go install filehandling
$]wrkspacepath/bin/filehandling -fpath=/path-of-file/test.txt

將輸出以下內容

Hel  
lo  
Wor  
ld.  
 We
lco  
me  
to  
fil  
e h  
and  
lin  
g i  
n G  
o.  
Error reading file: EOF  

逐行讀取檔案

在本節中,我們將討論如何使用Go逐行讀取檔案。 這可以使用bufio包完成。

請用以下內容替換test.txt中的內容

Hello World. Welcome to file handling in Go.  
This is the second line of the file.  
We have reached the end of the file.

以下是逐行讀取檔案所涉及的步驟。

  • 開啟檔案
  • 從檔案中建立一個新的掃描程式
  • 掃描檔案並逐行讀取。

用以下內容替換filehandling.go的內容

package main

import (  
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {  
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()

    f, err := os.Open(*fptr)
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err = f.Close(); err != nil {
        log.Fatal(err)
    }
    }()
    s := bufio.NewScanner(f)
    for s.Scan() {
        fmt.Println(s.Text())
    }
    err = s.Err()
    if err != nil {
        log.Fatal(err)
    }
}

在上面的程式中,我們使用從命令列標誌傳遞的路徑開啟檔案。在第24行我們使用該檔案建立一個新的掃描器。第25行為scan()的方法。通過text()方法可讀取檔案的下一行。

Scan返回false之後,Err()方法將返回掃描期間發生的任何錯誤,除非檔案讀取結束,Err()將返回nil

如果我們使用命令執行上面的程式,

$] go install filehandling
$] workspacepath/bin/filehandling -fpath=/path-of-file/test.txt

它會輸出

Hello World. Welcome to file handling in Go.  
This is the second line of the file.  
We have reached the end of the file.