87 votes

Comment 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")

Cela fonctionne bien, mais ce n'est pas vraiment portable. Dans mon cas, les fichiers que je veux ouvrir se trouvent dans mon GOPATH, par exemple :

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

Depuis l'entrée en vigueur de la data se trouve à côté du code source, j'aimerais pouvoir spécifier le chemin relatif :

data/file.txt

Mais j'obtiens alors cette erreur :

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

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

( Notez que ma question porte spécifiquement sur l'ouverture de fichiers relatifs au GOPATH. Ouvrir des fichiers en utilisant n'importe quel chemin relatif en Go est aussi simple que d'indiquer le chemin relatif au lieu du chemin absolu ; les fichiers sont ouverts relativement au répertoire de travail du binaire compilé. Dans mon cas, je veux ouvrir les fichiers relativement à l'endroit où le binaire a été compilé. Rétrospectivement, il s'agit d'une mauvaise décision de conception).

103voto

Matt Points 5202

Hmm... les path/filepath Le paquet a Abs() qui fait ce dont j'ai besoin (jusqu'à présent) bien qu'elle soit un peu gênante :

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

Ensuite, j'utilise absPath pour charger le fichier et cela fonctionne bien.

Notez que, dans mon cas, les fichiers de données se trouvent dans un paquet séparé de l'application main à partir duquel j'exécute le programme. Si tout était dans le même paquet, j'enlèverais l'en-tête ../mypackage/ . Comme ce chemin est évidemment relatif, différents programmes auront des structures différentes et devront ajuster ce chemin en conséquence.

S'il existe une meilleure façon d'utiliser des ressources externes avec un programme Go tout en le gardant portable, n'hésitez pas à contribuer à une autre réponse.

57voto

spencercooly Points 2468

Cela semble fonctionner assez bien :

import "os"
import "io/ioutil"

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

13voto

Alec Thomas Points 5815

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 des fichiers par le biais d'une couche de type VFS. Il est complètement portable, supporte l'ajout d'arbres de fichiers entiers, la compression, etc.

L'inconvénient est que vous avez besoin d'une étape intermédiaire pour construire les fichiers Go à partir des données sources. J'utilise habituellement make pour cela.

Voici comment itérer sur 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 concret de son utilisation dans mon GeoIP l'emballage. Les Makefile génère le code, et geoip.go utilise le VFS.

5voto

Danny Sullivan Points 1203

À partir de Go 1.16, vous pouvez utiliser l'option incorporer l'emballage. Cela vous permet d'intégrer les fichiers dans le programme Go en cours d'exécution.

Compte tenu de la structure du fichier :

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

Vous pouvez faire référence au fichier à l'aide d'une directive 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))
}

Ce programme s'exécutera avec succès quel que soit l'endroit où il est exécuté. Ceci est utile dans le cas où le fichier pourrait être appelé à partir de plusieurs endroits différents, par exemple, à partir d'un répertoire de test.

4voto

BurntSushi5 Points 1229

Je pense qu'Alec Thomas a apporté la réponse, mais d'après mon expérience, elle n'est pas infaillible. Un problème que j'ai rencontré avec la compilation des ressources dans le binaire est que la compilation peut nécessiter beaucoup de mémoire en fonction de la taille de vos ressources. S'ils sont petits, il n'y a probablement pas lieu de s'inquiéter. Dans mon cas particulier, un fichier de police de 1 Mo nécessitait environ 1 Go de mémoire pour la compilation. C'était un problème parce que je voulais qu'il soit accessible sur un Raspberry Pi. C'était avec Go 1.0 ; les choses se sont peut-être améliorées avec Go 1.1.

Dans ce cas particulier, j'opte donc pour l'utilisation de l'option go/build pour trouver le répertoire source du programme en fonction du chemin d'importation. Bien sûr, cela nécessite que vos cibles aient un chemin d'importation GOPATH et que la source est disponible. Ce n'est donc pas une solution idéale dans tous les cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X