2013-06-12 14 views
26

Sto usando io/ioutil di leggere un piccolo file di testo:Come posso aprire file usando percorsi relativi in ​​Go?

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

e che funziona bene, ma questo non è esattamente portatile. Nel mio caso, i file che voglio aprire sono nella mia GOPATH, ad esempio:

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

Dal momento che la cartella di data cavalca a destra a fianco del codice sorgente, mi piacerebbe per specificare solo il percorso relativo:

data/file.txt 

Ma allora ottengo questo errore:

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

Come posso aprire i file utilizzando il loro percorso relativo, specialmente se vivono a fianco il mio codice Go?

+5

Il GOPATH non ha un grande significato una volta che il programma viene compilato, e ancor meno quando si distribuirlo. –

+0

Quello che sembra voler sembrare più come l'incorporamento dei file nel programma compilato. –

+2

Tipo di ... tranne che voglio i file di dati separati dalla sorgente. I file di dati sono vitali per la funzionalità del programma. Quindi quando qualcuno tira giù il mio codice sorgente (con i file di dati lungo il lato) e lo compila e lo esegue, i file di dati vengono caricati utilizzando un percorso relativo perché esistono vicino al codice sorgente o vicino a dove il programma è in esecuzione. – Matt

risposta

40

Hmm ... il pacchetto path/filepath ha Abs() che fa quello che mi serve (finora) anche se è un po 'scomodo:

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

allora io uso absPath per caricare il file e funziona benissimo.

Si noti che, nel mio caso, i file di dati si trovano in un pacchetto separato dal pacchetto main da cui sto eseguendo il programma. Se fosse tutto nello stesso pacchetto, rimuoverei il numero iniziale ../mypackage/. Dato che questo percorso è ovviamente relativo, diversi programmi avranno strutture diverse e devono essere adattati di conseguenza.

Se esiste un modo migliore per utilizzare risorse esterne con un programma Go e mantenerlo portatile, sentiti libero di fornire un'altra risposta.

+0

Ha, quasi un anno dopo in Go, e non ho mai avuto problemi nell'aprire i file per percorso relativo da allora, né ho dovuto usare questo piccolo trucco. Immagino che questo significhi che ho imparato qualcosa. – Matt

+8

Che cosa hai imparato? :) Non dovremmo condividere file come questo sui pacchetti? – srt32

+3

@Matt, condividi l'amore. Non voglio usare questo "piccolo trucco" se hai qualcosa di meglio per noi. Sei il miglior risultato su google. – OGHaza

6

Ho scritto gobundle per risolvere esattamente questo problema. Genera il codice sorgente Go da file di dati, che vengono poi compilati nel file binario. È quindi possibile accedere ai dati del file tramite un livello simile a VFS. È completamente portatile, supporta l'aggiunta di interi alberi di file, compressione, ecc.

Lo svantaggio è che è necessario un passaggio intermedio per creare i file Vai dai dati di origine. Di solito uso make per questo.

Ecco come ci si iterazioni su tutti i file in un fascio, la lettura del byte:

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

Si può vedere un esempio reale del suo uso nel mio pacchetto GeoIP. Il Makefile genera il codice e geoip.go utilizza VFS.

+0

@ BurntSushi5 ha un buon punto; i miei file di dati hanno dimensioni di centinaia di MB. Questo è bello, ma non penso che la compilazione dei file di testo in chiaro nel binario sia un'opzione fattibile. – Matt

1

Credo che Alec Thomas abbia fornito la risposta, ma secondo la mia esperienza non è infallibile. Un problema che ho avuto con la compilazione delle risorse nel file binario è che la compilazione potrebbe richiedere molta memoria a seconda delle dimensioni delle risorse. Se sono piccoli, probabilmente non c'è nulla di cui preoccuparsi. Nel mio particolare scenario, un file di font da 1MB stava causando la compilazione per richiedere circa 1 GB di memoria da compilare. Era un problema perché volevo che fosse disponibile su un Raspberry Pi. Questo era con Go 1.0; le cose potrebbero essere migliorate in Go 1.1.

Quindi, in questo caso specifico, scelgo di utilizzare solo il pacchetto go/build per trovare il source directory del programma in base al percorso di importazione.Ovviamente, ciò richiede che i tuoi obiettivi abbiano una configurazione GOPATH e che la fonte sia disponibile. Quindi non è una soluzione ideale in tutti i casi.

12

Questo sembra funzionare abbastanza bene:

import "os" 
import "io/ioutil" 

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