Как я могу открывать файлы, относящиеся к моему GOPATH?

Я использую 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 так же просто, как указание относительного пути вместо абсолютного пути; файлы открываются относительно рабочего каталога скомпилированного двоичного файла. В моем случае я хочу открывать файлы относительно того места, где был скомпилирован двоичный файл. Оглядываясь назад, это плохое дизайнерское решение.)


person Matt    schedule 12.06.2013    source источник
comment
GOPATH не имеет большого значения после компиляции вашей программы и тем более при ее распространении.   -  person Denys Séguret    schedule 12.06.2013
comment
То, что вам кажется, больше похоже на встраивание файлов в вашу скомпилированную программу.   -  person Denys Séguret    schedule 12.06.2013
comment
Вроде ... за исключением того, что я хочу, чтобы файлы данных были отделены от источника. Файлы данных жизненно важны для функциональности программы. Поэтому, когда кто-то извлекает мой исходный код (с файлами данных рядом с ним), компилирует и запускает его, файлы данных загружаются с использованием относительного пути, потому что они существуют рядом с исходным кодом или рядом с тем местом, где выполняется программа.   -  person Matt    schedule 12.06.2013
comment
Скомпилированный двоичный файл не должен зависеть от расположения исходных файлов, но было бы неплохо, если бы существовал способ создания исполняемого пакета, содержащего копию внешних ресурсов, от которых могут зависеть пакеты.   -  person    schedule 12.06.2013
comment
@CrazyTrain Мне нравится ... Я новичок в Go, и мой типичный подход к этому (например, Python или PHP) состоит в том, чтобы связать файлы данных с исходным кодом и ссылаться на них относительно. Итак, что касается Go, у меня может быть реальный вопрос: как мне получить доступ к этим внешним ресурсам? (Если они отсутствуют, я бы поступил соответствующим образом.)   -  person Matt    schedule 12.06.2013
comment
@CrazyTrain - это то, что делает fileembed-go? (bitbucket.org/rj/fileembed-go)   -  person Rick-777    schedule 12.06.2013
comment
Вот связанный с этим вопрос об объединении ресурсов, которого может быть достаточно, хотя в моем случае это не мой предпочтительный метод: stackoverflow.com/questions/13904441/ - или вот этот: stackoverflow.com/q/9443418/1048862   -  person Matt    schedule 12.06.2013
comment
Проверьте с помощью pwd, _: = os.Getwd () ваш рабочий каталог, затем перейдите по пути, например, при открытии содержимого файла, err: = os.OpenFile ((.. \\ .. \\ pkg \\ service \\ profile \ \ TTL.json), os.O_RDONLY, 0755)   -  person infiniteLearner    schedule 06.05.2021


Ответы (4)


Хм ... в пакете path/filepath есть Abs(), который делает то, что мне нужно (пока), хотя это немного неудобно :

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

Затем я использую absPath для загрузки файла, и он отлично работает.

Обратите внимание, что в моем случае файлы данных находятся в пакете, отдельном от пакета main, из которого я запускаю программу. Если бы все это было в одном пакете, я бы удалил ведущий ../mypackage/. Поскольку этот путь, очевидно, относительный, разные программы будут иметь разную структуру и потребуют соответствующей корректировки.

Если есть лучший способ использовать внешние ресурсы с программой Go и сохранить ее переносимость, не стесняйтесь предлагать другой ответ.

person Matt    schedule 12.06.2013
comment
Ха, почти год спустя в Go, и с тех пор у меня никогда не было проблем с открытием файлов по относительному пути, и мне не приходилось использовать этот небольшой трюк. Думаю, это означает, что я кое-чему научился. - person Matt; 24.03.2014
comment
Что вы узнали? :) Разве мы не должны делиться такими файлами между пакетами? - person srt32; 20.09.2014
comment
@ Мэтт, поделись любовью. Не хочу использовать эту маленькую уловку, если у тебя есть для нас что-то получше. Вы лучший результат в Google. - person OGHaza; 18.11.2016
comment
Этот вопрос был задан в первые недели моего программирования на Go. Лучшим вопросом было бы, как связать необходимые данные с программой. Мой ответ здесь для моей очень конкретной ситуации, я надеюсь, что люди не уйдут и не используют это случайно. - person Matt; 20.11.2016
comment
@Matt Я столкнулся с этой проблемой, потому что в зависимости от того, как я запускаю программу / тесты в GoLand, изменяется рабочий каталог и, следовательно, путь к моему файлу. Если это согласуется, то, вероятно, нет причин находить абсолютный путь при каждом выполнении. - person wyatt; 07.12.2018
comment
Я прочитал это и подумал, что мне нужно расширить относительные пути для правильной работы ioutil.ReadFile, но в этом нет необходимости. Это нормально работает: ioutil.ReadFile("../mypackage/data/file.txt"). Вероятно, есть много других случаев, когда вы хотите построить абсолютный путь, но похоже, что использование с io.ReadFile не входит в их число. - person Davos; 02.05.2019
comment
@Davos Я думаю, что относительные пути разрешены относительно cwd. Что будет, если вы запустите программу из другого каталога? Например, go run ./some/directory/main.go вместо go run main.go - person Aruna Herath; 20.03.2021
comment
@ArunaHerath, вы можете убедиться в этом, попробовав - person Davos; 23.03.2021
comment
@Davos Проверено. Если вы запустите свою программу из другого каталога, она не будет работать. Поскольку файлы разрешаются относительно текущего рабочего каталога. - person Aruna Herath; 24.03.2021

похоже, это работает очень хорошо:

import "os"
import "io/ioutil"

pwd, _ := os.Getwd()
txt, _ := ioutil.ReadFile(pwd+"/path/to/file.txt")
person spencercooly    schedule 16.07.2015
comment
Это плохая форма, потому что / зависит от ОС, лучше использовать filepath.join () при объединении путей, потому что он будет использовать os.PathSeperator - person Ace.C; 12.05.2018
comment
Позвольте мне сэкономить время для тех, кто хочет последовать (хорошему) совету из предыдущего комментария: используйте import "path/filepath" и позвоните filepath.Join(...) (с большой буквы J). :-) - person Bojan Komazec; 28.12.2018
comment
os.Open(pwd + "../mocks/product/default.json") как вы справляетесь с этим делом? он воспринимает это буквально. - person kamal; 19.02.2019
comment
Для меня os.Open подходит: csvfile, err: = os.Open (/mnt/c/Users/dmotta/Downloads/archivos_csv/tb_desp_new_201904081633.csv) - person dmotta; 20.04.2019

Я написал 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 генерирует код, а _ 3_ использует VFS.

person Alec Thomas    schedule 14.06.2013
comment
@ BurntSushi5 имеет хороший аргумент; мои файлы данных имеют размер в сотни МБ. Это круто, но я не думаю, что компилирование текстовых файлов в двоичный файл является возможным вариантом. - person Matt; 14.06.2013

Я думаю, что Алек Томас дал ответ, но, по моему опыту, он не является надежным. Одна из проблем, с которыми я столкнулся при компиляции ресурсов в двоичный файл, заключается в том, что для компиляции может потребоваться много памяти в зависимости от размера ваших ресурсов. Если они маленькие, то, наверное, не о чем беспокоиться. В моем конкретном сценарии файл шрифта размером 1 МБ приводил к тому, что для компиляции требовалось около 1 ГБ памяти для компиляции. Это было проблемой, потому что я хотел, чтобы его можно было использовать на Raspberry Pi. Это было с Go 1.0; возможно, в Go 1.1 ситуация улучшилась.

Поэтому в этом конкретном случае я предпочитаю просто использовать пакет go/build, чтобы найти исходный каталог программы на основе пути импорта. Конечно, для этого необходимо, чтобы у ваших целей была GOPATH настроена и чтобы источник был доступен. Так что это не во всех случаях идеальное решение.

person BurntSushi5    schedule 14.06.2013