Wie kann ich Dateien relativ zu meinem GOPATH öffnen?

72

Ich verwende 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 tragbar. In meinem Fall befinden sich die Dateien, die ich öffnen möchte, in meinem GOPATH, zum Beispiel:

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

Da der dataOrdner direkt neben dem Quellcode angezeigt wird, möchte ich nur den relativen Pfad angeben:

data/file.txt

Aber dann bekomme ich diesen Fehler:

Panik: Öffnen Sie data / file.txt: Keine solche Datei oder kein solches Verzeichnis

Wie kann ich Dateien über ihren relativen Pfad öffnen, insbesondere wenn sie neben meinem Go-Code gespeichert sind?

( Beachten Sie, dass es bei meiner Frage speziell um das Öffnen von Dateien relativ zum GOPATH geht. Das Öffnen von Dateien mit einem beliebigen relativen Pfad in Go ist so einfach wie das Angeben des relativen Pfads anstelle eines absoluten Pfads. Dateien werden relativ zum Arbeitsverzeichnis der kompilierten Binärdatei geöffnet In diesem Fall möchte ich Dateien öffnen, die sich darauf beziehen, wo die Binärdatei kompiliert wurde. Im Nachhinein ist dies eine schlechte Entwurfsentscheidung.)

Matt
quelle
7
Der GOPATH hat keine große Bedeutung, sobald Ihr Programm kompiliert ist, und noch weniger, wenn Sie es verteilen.
Denys Séguret
Was Sie zu wollen scheinen, sieht eher nach einer Einbettung der Dateien in Ihr kompiliertes Programm aus.
Denys Séguret
2
Art von ... außer ich möchte, dass die Datendateien von der Quelle getrennt sind. Die Datendateien sind für die Funktionalität des Programms von entscheidender Bedeutung. Wenn also jemand meinen Quellcode (mit den Datendateien daneben) herunterzieht und kompiliert und ausführt, werden die Datendateien unter Verwendung eines relativen Pfads geladen, da sie in der Nähe des Quellcodes oder in der Nähe des Ausführungsortes des Programms vorhanden sind.
Matt
1
Die kompilierte Binärdatei sollte keine Abhängigkeit vom Speicherort der Quelldateien haben, aber es wäre schön, wenn es eine Möglichkeit gäbe, ein ausführbares Bundle zu erstellen, das eine Kopie externer Ressourcen enthält, von denen Pakete abhängen können.
1
Hier ist eine verwandte Frage zum Bündeln von Ressourcen, die möglicherweise ausreichend ist, obwohl dies in meinem Fall nicht meine bevorzugte Methode ist: stackoverflow.com/questions/13904441/… - oder diese: stackoverflow.com/q/9443418/1048862
Matt

Antworten:

88

Hmm ... das path/filepathPaket hat Abs()das, was ich (bisher) brauche, obwohl es etwas unpraktisch ist:

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

Dann absPathlade ich die Datei und sie funktioniert einwandfrei.

Beachten Sie, dass sich die Datendateien in meinem Fall in einem Paket befinden, das von dem mainPaket getrennt ist, von dem aus ich das Programm ausführe. Wenn alles im selben Paket wäre, würde ich das führende entfernen ../mypackage/. 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.

Matt
quelle
Ha, fast ein Jahr später in Go, und ich hatte seitdem nie mehr Probleme, Dateien über einen relativen Pfad zu öffnen, noch musste ich diesen kleinen Trick anwenden. Ich denke, das bedeutet, dass ich etwas gelernt habe.
Matt
11
Was hast du gelernt? :) Sollten wir solche Dateien nicht paketübergreifend teilen?
srt32
6
@ Matt, teile die Liebe. Ich möchte diesen "kleinen Trick" nicht anwenden, wenn Sie etwas Besseres für uns haben. Du bist das Top-Ergebnis bei Google.
OGHaza
3
Diese Frage stammte aus meiner ersten Programmierwoche in Go. Die bessere Frage wäre gewesen, wie man die erforderlichen Daten mit einem Programm bündelt. Meine Antwort hier ist für meine sehr spezifische Situation, ich hoffe, die Leute gehen nicht weg und benutzen dies willkürlich.
Matt
1
Ich habe das gelesen und dachte, ich ioutil.ReadFilemüsste die relativen Pfade erweitern , um richtig zu arbeiten, aber es ist völlig unnötig. Das funktioniert gut : ioutil.ReadFile("../mypackage/data/file.txt"). Es gibt wahrscheinlich viele andere Fälle, in denen Sie einen absoluten Pfad erstellen möchten, aber es scheint, dass die Verwendung mit io.ReadFilenicht einer von ihnen ist.
Davos
40

das scheint ziemlich gut zu funktionieren:

import "os"
import "io/ioutil"

pwd, _ := os.Getwd()
txt, _ := ioutil.ReadFile(pwd+"/path/to/file.txt")
spencercooly
quelle
13
Dies ist eine schlechte Form, da / os-spezifisch ist. Verwenden Sie beim Verketten von Pfaden besser filepath.join (), da os.PathSeperator
Ace.C
7
Lassen Sie mich die Zeit für diejenigen sparen, die bereit sind, den (guten) Ratschlägen aus dem vorherigen Kommentar zu folgen: Verwenden import "path/filepath"und Anrufen filepath.Join(...)(mit Kapital J). :-)
Bojan Komazec
2
os.Open(pwd + "../mocks/product/default.json")Wie gehen Sie mit diesem Fall um? es nimmt dies als wörtlich.
Kamal
Das os.Open ist in Ordnung für mich: csvfile, err: = os.Open ("/ mnt / c / Benutzer / dmotta / Downloads / archivos_csv / tb_desp_new_201904081633.csv")
dmotta
11

Ich habe Gobundle geschrieben , 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, die Komprimierung usw.

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

So würden Sie alle Dateien in einem Bundle durchlaufen und die Bytes 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 die Verwendung in meinem GeoIP- Paket sehen. Der Makefilegeneriert den Code und geoip.goverwendet das VFS.

Alec Thomas
quelle
@ BurntSushi5 hat einen guten Punkt; Meine Datendateien sind Hunderte von MB groß. Das ist cool, aber ich denke nicht, dass das Kompilieren der Nur-Text-Dateien in die Binärdatei eine praktikable Option ist.
Matt
4

Ich denke, Alec Thomas hat die Antwort geliefert, aber meiner Erfahrung nach ist sie nicht narrensicher. Ein Problem beim Kompilieren von Ressourcen in die Binärdatei besteht darin, 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 beim Kompilieren etwa 1 GB Arbeitsspeicher. Es war ein Problem, weil ich wollte, dass es auf einem Raspberry Pi verfügbar ist. Dies war mit Go 1.0; Möglicherweise haben sich die Dinge in Go 1.1 verbessert.

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

BurntSushi5
quelle