web-dev-qa-db-fra.com

Comment puis-je ouvrir des fichiers relatifs à mon GOPATH?

J'utilise io/ioutil pour lire un petit fichier texte:

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

Et cela fonctionne bien, mais ce n’est pas vraiment portable. Dans mon cas, les fichiers que je veux ouvrir sont dans mon GOPATH, par exemple:

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

Puisque le dossier data passe juste à côté du code source, j'aimerais simplement spécifier le chemin relatif:

data/file.txt

Mais alors je reçois cette erreur:

panic: open data/file.txt: aucun fichier ni répertoire de ce type

Comment puis-je ouvrir des fichiers en utilisant leur chemin relatif, en particulier s'ils vivent à côté de mon code Go?

(Remarque: ma question porte spécifiquement sur l'ouverture de fichiers relatifs à GOPATH. Ouvrir des fichiers à l'aide d'un chemin relatif dans Go est aussi simple que de donner le chemin relatif à la place d'un fichier. chemin absolu; les fichiers sont ouverts par rapport au répertoire de travail du binaire compilé. Dans mon cas, je veux ouvrir les fichiers relatifs au lieu où le binaire a été compilé. Avec le recul, ceci est une mauvaise décision de conception.)

63
Matt

Hmm ... le paquet path/filepath A Abs() qui fait ce dont j'ai besoin (jusqu'à présent) bien que ce soit un peu gênant:

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

Ensuite, j’utilise absPath pour charger le fichier et cela fonctionne correctement.

Notez que, dans mon cas, les fichiers de données sont dans un package distinct du package main à partir duquel j'exécute le programme. Si tout était dans le même package, je supprimerais le premier ../mypackage/. Comme ce chemin est évidemment relatif, différents programmes auront des structures différentes et devront être ajustés en conséquence.

S'il existe un meilleur moyen d'utiliser des ressources externes avec un programme Go et de le garder portable, n'hésitez pas à apporter une autre réponse.

74
Matt

cela semble bien fonctionner:

import "os"
import "io/ioutil"

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

J'ai écrit gobundle pour résoudre exactement ce problème. Il génère le code source Go à partir de fichiers de données, que vous compilez ensuite dans votre binaire. Vous pouvez ensuite accéder aux données du fichier via une couche de type VFS. Il est entièrement portable et prend en charge l’ajout d’arbres de fichiers complets, la compression, etc.

L'inconvénient est qu'il vous faut une étape intermédiaire pour créer les fichiers Go à partir des données source. J'utilise habituellement make pour cela.

Voici comment vous pouvez parcourir tous les fichiers d'un paquet, en lisant les octets:

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

Vous pouvez voir un exemple réel de son utilisation dans mon package GeoIP . Le Makefile génère le code et geoip.go utilise le VFS.

9
Alec Thomas

Je pense que Alec Thomas a fourni The Answer, mais d'après mon expérience, il n'est pas infaillible. Un problème que j'ai rencontré lors de la compilation des ressources dans le fichier binaire est que la compilation peut nécessiter beaucoup de mémoire, en fonction de la taille de vos actifs. S'ils sont petits, il n'y a probablement rien à craindre. Dans mon scénario particulier, un fichier de police de 1 Mo faisait en sorte que la compilation nécessite environ 1 Go de mémoire pour la compilation. C'était un problème parce que je voulais que ce soit facile à utiliser avec un Raspberry Pi. C'était avec Go 1.0; les choses peuvent s'être améliorées dans Go 1.1.

Donc, dans ce cas particulier, je choisis simplement d'utiliser le go/build package pour trouver le répertoire source du programme en fonction du chemin d’importation. Bien entendu, cela nécessite que vos cibles aient une configuration GOPATH et que la source soit disponible. Ce n'est donc pas une solution idéale dans tous les cas.

3
BurntSushi5