2013-06-12 141 views
26

我使用io/ioutil閱讀的小文本文件:如何在Go中使用相對路徑打開文件?

fileBytes, err := ioutil.ReadFile("/absolute/path/to/file.txt") 

這工作得很好,但是這不完全便攜。在我的情況,我想打開這些文件都在我的GOPATH,例如:

/Users/matt/Dev/go/src/github.com/mholt/mypackage/data/file.txt 

由於data文件夾騎右側旁邊的源代碼,我喜歡只指定相對路徑:

data/file.txt 

但後來我得到這個錯誤:

panic: open data/file.txt: no such file or directory

我怎樣才能打開使用他們的相對路徑的文件,特別是如果他們一起生活我的Go代碼?

+5

的GOPATH還沒有大的意思,一旦你的程序被編譯,即使你分發少。 –

+0

你似乎想要的東西看起來更像是在編譯的程序中嵌入文件。 –

+2

...的種類,但我希望數據文件與源分離。數據文件對程序的功能至關重要。因此,當有人拉下我的源代碼(數據文件沿着它)並編譯並運行它時,數據文件將使用相對路徑加載,因爲它們存在於源代碼附近,或者靠近程序執行的地方。 – Matt

回答

40

嗯...的path/filepath包有Abs()它做什麼,我需要(到目前爲止)雖然這是一個有點不方便:

absPath, _ := filepath.Abs("../mypackage/data/file.txt") 

然後我用absPath加載的文件,它工作正常。

請注意,就我而言,數據文件位於與運行該程序的main軟件包不同的軟件包中。如果它全部在同一個包中,我會刪除前導../mypackage/。由於這條道路顯然是相對的,所以不同的方案會有不同的結構,需要相應調整。

如果有更好的方式使用Go程序使用外部資源並保持便攜性,請隨時提供其他答案。

+0

哈,幾乎一年後進入Go,而我從來沒有遇到過用相對路徑打開文件的問題,我也不必使用這個小竅門。我想這意味着我學到了一些東西。 – Matt

+8

你學到了什麼? :)我們不應該在軟件包之間共享這樣的文件嗎? – srt32

+3

@Matt,分享愛。如果你有更好的東西給我們,不要使用這個「小竅門」。你是谷歌最重要的結果。 – OGHaza

6

我寫了gobundle來解決這個問題。它從數據文件生成Go源代碼,然後將其編譯到二進制文件中。然後您可以通過類似VFS的層訪問文件數據。它是完全便攜的,支持添加整個文件樹,壓縮等。

缺點是您需要一箇中間步驟來從源數據構建Go文件。我通常使用make來做這件事。

這裏是你如何通過遍歷所有文件捆綁,讀取字節:

for _, name := range bundle.Files() { 
    r, _ := bundle.Open(name) 
    b, _ := ioutil.ReadAll(r) 
    fmt.Printf("file %s has length %d\n", name, len(b)) 
} 

你可以看到在我GeoIP包其使用的一個真實的例子。 Makefile生成代碼,geoip.go使用VFS。

+0

@ BurntSushi5有一個好點;我的數據文件的大小是數百MB。這很酷,但我不認爲將純文本文件編譯爲二進制文件是一種可行的選擇。 – Matt

1

我認爲亞歷克托馬斯提供了答案,但根據我的經驗,這不是萬無一失的。編譯資源到二進制文件時遇到的一個問題是編譯可能需要大量內存,具體取決於資源的大小。如果他們很小,那麼可能沒有什麼可擔心的。在我的特殊情況下,1MB字體文件導致編譯需要大約1GB的內存進行編譯。這是一個問題,因爲我希望它能在樹莓派上得到解決。這與Go 1.0一樣; Go 1.1中的內容可能會有所改進。

因此,在這種特殊情況下,我選擇使用go/build包來根據導入路徑找到程序的source directory。當然,這要求您的目標設置爲GOPATH,並且源代碼可用。所以這在所有情況下都不是理想的解決方案。

12

這似乎工作得很好:

import "os" 
import "io/ioutil" 

pwd, _ := os.Getwd() 
txt, _ := ioutil.ReadFile(pwd+"/path/to/file.txt") 
相關問題