如何打开相对于GOPATH的文件?

pftdvrlh  于 2023-01-06  发布在  Go
关注(0)|答案(5)|浏览(110)

我使用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语言中使用任何相对路径打开文件都很简单,只要给出相对路径而不是绝对路径即可;文件相对于编译后的二进制文件的工作目录打开。在我的例子中,我希望相对于二进制文件编译的位置打开文件。事后看来,这是一个糟糕的设计决策。)
fivyi3re

fivyi3re1#

嗯...... path/filepath软件包有Abs(),它满足了我的需求(到目前为止),尽管有点不方便:

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

然后我使用absPath加载文件,它工作正常。
注意,在我的例子中,数据文件在一个独立于main包的包中,如果它们都在同一个包中,我会删除前面的../mypackage/,因为这个路径显然是相对的,不同的程序将有不同的结构,需要相应地调整。
如果有更好的方法来使用外部资源并保持程序的可移植性,请随意给出另一个答案。

jm2pwxwz

jm2pwxwz2#

这看起来工作得很好:

import "os"
import "io/ioutil"

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

rqenqsqc3#

我编写gobundle就是为了解决这个问题。它从数据文件中生成Go语言源代码,然后编译成二进制文件。然后可以通过一个类似于VFS的层访问文件数据。它是完全可移植的,支持添加整个文件树、压缩等。
缺点是您需要一个中间步骤来从源数据构建Go文件,我通常使用make来实现这一点。
下面是如何遍历bundle中的所有文件,阅读字节:

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。

8nuwlpux

8nuwlpux4#

从Go语言1.16开始,可以使用embed包,这样就可以把文件嵌入到正在运行的Go语言程序中。
给定文件结构:

-- main.go
-- data
  \- file.txt

可以使用go指令引用该文件

package main

import (
  "embed"
  "fmt"
)

//go:embed data/file.txt
var content embed.FS

func main() {
  text, _ := content.ReadFile("data/file.txt")
  fmt.Println(string(text))
}

无论程序在何处执行,此程序都将成功运行。这在可以从多个不同位置调用文件的情况下很有用,例如,从测试目录。

ajsxfq5m

ajsxfq5m5#

我认为Alec托马斯已经提供了答案,但根据我的经验,这并不是万无一失的。我在将资源编译成二进制文件时遇到的一个问题是,根据资源的大小,编译可能需要大量内存。如果资源很小,那么可能就没什么可担心的。在我的特定场景中,一个1 MB的字体文件导致编译需要大约1GB的内存。这是一个问题,因为我希望它可以在树莓派上获得。这是Go语言1.0;在Go语言1.1中可能有所改进。
因此,在这种特殊情况下,我选择只使用go/build包根据导入路径查找程序的源目录。当然,这要求您的目标设置了GOPATH,并且源文件可用。因此,它不是在所有情况下都是理想的解决方案。

相关问题