如何打开与我的GOPATH相关的文件?


73

我正在io/ioutil读取一个小的文本文件:

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

但是然后我得到这个错误:

恐慌:打开数据/文件。txt:没有此类文件或目录

如何使用文件的相对路径打开文件,尤其是当它们与我的Go代码一起存在时?

请注意,我的问题特别是关于打开相对于GOPATH的文件在Go中使用任何相对路径打开文件就像提供相对路径而不是绝对路径一样容易;文件是相对于已编译二进制文件的工作目录打开的。在这种情况下,我想打开相对于二进制文件编译位置的文件。事后看来,这是一个错误的设计决定。)


7
一旦您的程序被编译,GOPATH就没有太大的意义了,而在您分发它时就没有多大的意义了。
DenysSéguret13年

您似乎想要的看起来更像是将文件嵌入到已编译程序中。
DenysSéguret13年

2
有点...除了我希望数据文件与源分开。数据文件对于程序的功能至关重要。因此,当有人拉下我的源代码(数据文件并排放置)并编译并运行它时,由于数据文件存在于源代码附近或程序执行位置附近,因此它们使用相对路径加载。
马特

1
编译后的二进制文件应该不依赖于源文件的位置,但是如果有一种方法可以创建一个可执行文件包,该文件包包含一个可能依赖于包的外部资源的副本,那就太好了。

1
这是一个有关捆绑资源的相关问题,尽管这不是我的首选方法,但是这可能足以解决这个问题:stackoverflow.com/questions/13904441/…-或这个:stackoverflow.com/q/9443418/1048862
Matt

Answers:


88

嗯。。。虽然有点不方便,但是path/filepath包装有满足Abs()我需要的东西(到目前为止):

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

然后,我使用它absPath来加载文件,并且工作正常。

请注意,就我而言,数据文件位于与main我正在运行程序的程序包不同的程序包中。如果所有包装都在同一个包装中,我将删除前导../mypackage/。由于该路径显然是相对的,因此不同的程序将具有不同的结构,并且需要对此进行相应的调整。

如果有更好的方法可以将外部资源与Go程序一起使用并保持可移植性,请随时提出其他答案。


哈,差不多一年后才进入Go,从那以后我从没有通过相对路径打开文件的问题,也不必使用这个小技巧。我想这意味着我学到了一些东西。
马特

11
你学到了什么?:)我们不应该在软件包之间共享这样的文件吗?
srt32

6
@Matt,分享爱。如果您对我们有更好的帮助,则不想使用此“小技巧”。您是Google的最佳搜索结果。
OGHaza

3
这个问题来自我在Go中编程的最初几周。更好的问题是如何将所需数据与程序捆绑在一起。我的回答是针对我的具体情况,希望人们不要随意使用它。
马特

1
我读了这篇文章,以为我必须扩展相对路径ioutil.ReadFile才能正常工作,但这完全没有必要。可以正常工作:ioutil.ReadFile("../mypackage/data/file.txt")。可能还有很多其他情况需要构造绝对路径,但似乎与with一起使用io.ReadFile并不是其中一种。
达沃斯

40

这似乎工作得很好:

import "os"
import "io/ioutil"

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

13
这是一种不好的形式,因为/特定于os,最好在连接路径时使用filepath.join(),因为它将使用os.PathSeperator
Ace.C

7
让我为那些愿意听从先前评论中的(好的)建议的人节省时间:使用import "path/filepath"和致电filepath.Join(...)(大写J)。:-)
Bojan Komazec

2
os.Open(pwd + "../mocks/product/default.json")您如何处理此案?它以此为文字。
kamal

os.Open对我来说很合适:csvfile,err:= os.Open(“ / mnt / c / Users / dmotta / Downloads / archivos_csv / tb_desp_new_201904081633.csv”)
dmotta

11

我写了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。


@ BurntSushi5有一个好处;我的数据文件大小为数百MB。这很酷,但是我认为将纯文本文件编译为二进制文件不是可行的选择。
马特

4

我认为亚历克·托马斯(Alec Thomas)提供了答案,但是根据我的经验,这并不是万无一失的。我将资源编译为二进制文件时遇到的一个问题是,编译可能需要大量内存,具体取决于资产的大小。如果它们很小,那么就不用担心了。在我的特定情况下,一个1MB的字体文件导致编译需要大约1GB的内存才能进行编译。这是一个问题,因为我希望它可以在Raspberry Pi上变得可获取。这是Go 1.0的版本;Go 1.1中的情况可能有所改善。

因此,在这种特殊情况下,我选择仅使用该go/build软件包根据导入路径查找程序的源目录。当然,这需要您的目标已GOPATH设置并且源可用。因此,它并非在所有情况下都是理想的解决方案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.