it-swarm.com.de

Wie kann ich Dateien relativ zu meinem GOPATH öffnen?

Ich benutze io/ioutil um eine kleine Textdatei zu lesen:

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

Und das funktioniert gut, aber das ist nicht gerade portabel. In meinem Fall befinden sich die zu öffnenden Dateien in meinem GOPATH, zum Beispiel:

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

Da sich der Ordner data direkt neben dem Quellcode befindet, möchte ich nur den relativen Pfad angeben:

data/file.txt

Aber dann bekomme ich diesen Fehler:

panic: open data/file.txt: keine solche Datei oder Verzeichnis

Wie kann ich Dateien unter Verwendung ihres relativen Pfads öffnen, insbesondere wenn sie sich neben meinem Go-Code befinden?

(Beachten Sie, dass es in meiner Frage speziell um das Öffnen von Dateien relativ zum GOPATH geht. Das Öffnen von Dateien unter Verwendung eines relativen Pfads in Go ist so einfach wie das Angeben des relativen Pfads anstelle eines absoluter Pfad; Dateien werden relativ zum Arbeitsverzeichnis der kompilierten Binärdatei geöffnet. In meinem Fall möchte ich Dateien relativ zu dem Kompilierungsort der Binärdatei öffnen. Im Nachhinein ist dies eine falsche Entwurfsentscheidung.)

63
Matt

Hmm ... das Paket path/filepath Hat Abs() was ich (bisher) brauche, obwohl es ein bisschen unpraktisch ist:

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

Dann benutze ich absPath, um die Datei zu laden und es funktioniert gut.

Beachten Sie, dass sich die Datendateien in meinem Fall in einem Paket befinden, das vom main -Paket getrennt ist, von dem aus ich das Programm ausführe. Wenn alles im selben Paket wäre, würde ich den führenden ../mypackage/ Entfernen. Da dieser Pfad offensichtlich relativ ist, haben verschiedene Programme unterschiedliche Strukturen und müssen entsprechend angepasst werden.

Wenn es eine bessere Möglichkeit gibt, externe Ressourcen mit einem Go-Programm zu nutzen und portabel zu halten, können Sie eine weitere Antwort hinzufügen.

74
Matt

das scheint ziemlich gut zu funktionieren:

import "os"
import "io/ioutil"

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

Ich schrieb gobundle um genau dieses Problem zu lösen. Es generiert Go-Quellcode aus Datendateien, die Sie dann in Ihre Binärdatei kompilieren. Sie können dann über eine VFS-ähnliche Ebene auf die Dateidaten zugreifen. Es ist vollständig portabel und unterstützt das Hinzufügen ganzer Dateibäume, Komprimierung usw.

Der Nachteil ist, dass Sie einen Zwischenschritt benötigen, um die Go-Dateien aus den Quelldaten zu erstellen. Ich benutze normalerweise make dafür.

Gehen Sie wie folgt vor, um alle Dateien in einem Bundle zu durchlaufen und die Bytes zu lesen:

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

Sie können ein echtes Beispiel für seine Verwendung in meinem GeoIP -Paket sehen. Das Makefile generiert den Code und geoip.go verwendet das VFS.

9
Alec Thomas

Ich denke, Alec Thomas hat die Antwort geliefert, aber meiner Erfahrung nach ist sie nicht narrensicher. Ein Problem, das ich beim Kompilieren von Ressourcen in die Binärdatei hatte, ist, dass das Kompilieren abhängig von der Größe Ihrer Assets möglicherweise viel Speicher benötigt. Wenn sie klein sind, gibt es wahrscheinlich keinen Grund zur Sorge. In meinem speziellen Szenario verursachte eine 1-MB-Schriftartdatei, dass die Kompilierung etwa 1 GB Arbeitsspeicher benötigte. Es war ein Problem, weil ich wollte, dass es auf einem Raspberry Pi erhältlich ist. Dies war mit Go 1.0; In Go 1.1 haben sich die Dinge möglicherweise verbessert.

In diesem speziellen Fall verwende ich einfach das go/build Paket, um das Quellverzeichnis des Programms basierend auf dem Importpfad zu finden. Dies setzt natürlich voraus, dass für Ihre Ziele ein GOPATH eingerichtet ist und die Quelle verfügbar ist. Es ist also nicht in allen Fällen eine ideale Lösung.

3
BurntSushi5