2013-06-12 4 views
26

ich io/ioutil bin mit einer kleinen Textdatei zu lesen:Wie kann ich Dateien mit relativen Pfaden in Go öffnen?

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

Und das funktioniert gut, aber dies ist nicht gerade tragbar. In meinem Fall sind die Dateien, die ich öffnen will in meinem GOPATH, zum Beispiel:

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

Da die data Ordner direkt neben dem Quellcode reiten, würde ich nur den relativen Pfad angeben, gerne:

data/file.txt 

Aber dann bekomme ich diesen Fehler:

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

Wie kann ich Dateien öffnen ihre relativen Pfad, vor allem, wenn sie neben meinem Go-Code leben?

+5

Die GOPATH hat keine große Bedeutung, wenn Ihr Programm kompiliert wird, und noch weniger, wenn Sie es verteilen. –

+0

Was Sie anscheinend wollen, sieht mehr wie ein Einbetten der Dateien in Ihrem kompilierten Programm aus. –

+2

Art von ... außer ich möchte die Dateien getrennt von der Quelle. Die Datendateien sind für die Funktionalität des Programms entscheidend. Wenn also jemand meinen Quellcode herunterzieht (zusammen mit den Datendateien) und kompiliert und ausführt, werden die Datendateien mit einem relativen Pfad geladen, da sie in der Nähe des Quellcodes oder in der Nähe des Programms vorhanden sind. – Matt

Antwort

40

Hmm ... das path/filepath Paket hat Abs() was tut, was ich brauche (bisher), obwohl es ein wenig unbequem ist:

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

ich dann absPath die Datei zu laden und es funktioniert gut.

Beachten Sie, dass in meinem Fall die Datendateien in einem separaten Paket von dem main-Paket enthalten sind, von dem ich das Programm ausführe. Wenn alles im selben Paket wäre, würde ich die führende ../mypackage/ entfernen. Da dieser Pfad offensichtlich relativ ist, haben unterschiedliche Programme unterschiedliche Strukturen und müssen entsprechend angepasst werden.

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

+0

Ha, fast a Jahr später in Go, und ich hatte nie ein Problem, Dateien seit dem relativen Pfad zu öffnen, noch musste ich diesen kleinen Trick verwenden. Ich denke, das bedeutet, dass ich etwas gelernt habe. – Matt

+8

Was hast du gelernt? :) Sollten wir Dateien nicht in Paketen teilen? – srt32

+3

@Matt, teilen Sie die Liebe. Ich möchte diesen "kleinen Trick" nicht verwenden, wenn Sie etwas besseres für uns haben. Du bist das Top-Ergebnis bei Google. – OGHaza

6

Ich schrieb gobundle 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, 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. Normalerweise benutze ich dafür.

Hier ist, wie Sie in einem Bündel alle Dateien iterieren würde, das Lesen der Bytes:

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. Die Makefile generiert den Code und geoip.go verwendet das VFS.

+0

@ 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 machbare Option ist. – Matt

1

Ich denke Alec Thomas hat die Antwort zur Verfügung gestellt, aber nach meiner Erfahrung ist es 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 viel Speicher erfordert. Wenn sie klein sind, ist es wahrscheinlich nichts, worüber man sich Sorgen machen muss. In meinem speziellen Szenario verursachte eine 1-MB-Font-Datei, dass Kompilierung erforderlich ist, um etwa 1 GB Speicher zu kompilieren. Es war ein Problem, weil ich wollte, dass es auf einem Raspberry Pi gettierbar ist. Das war mit Go 1.0; Die Dinge könnten sich in Go 1.1 verbessert haben.

Also in diesem speziellen Fall, ich entscheide mich, einfach das go/build Paket zu verwenden, um die source directory des Programms basierend auf dem Importpfad zu finden.Natürlich erfordert dies, dass Ihre Ziele eine GOPATH eingerichtet haben und dass die Quelle verfügbar ist. Es ist also keine ideale Lösung in allen Fällen.

12

dies scheint ziemlich gut zu funktionieren:

import "os" 
import "io/ioutil" 

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