Come leggere / scrivere da / su file usando Go?
Domanda
Ho cercato di imparare Go per conto mio, ma sono stato sconcertato nel provare a leggere e scrivere su file ordinari.
Posso arrivare fino a inFile, _: = os.Open (INFILE, 0, 0)
, ma in realtà ottenere il contenuto del file non ha senso, perché la funzione di lettura accetta un [] byte
come parametro.
func (file *File) Read(b []byte) (n int, err Error)
Soluzione
Facciamo un elenco compatibile con Go 1 di tutti i modi per leggere e scrivere file in Go.
Poiché l'API dei file è cambiata di recente e la maggior parte delle altre risposte non funziona con Go 1. Mancano anche bufio
che è importante IMHO.
Negli esempi seguenti copio un file leggendo da esso e scrivendo nel file di destinazione.
Inizia con le basi
package main
import (
"io"
"os"
)
func main() {
// open input file
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// close fi on exit and check for its returned error
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()
// open output file
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
// make a buffer to keep chunks that are read
buf := make([]byte, 1024)
for {
// read a chunk
n, err := fi.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}
// write a chunk
if _, err := fo.Write(buf[:n]); err != nil {
panic(err)
}
}
}
Qui ho usato os.Open
e os.Create
che sono utili wrapper per os.OpenFile
. Di solito non abbiamo bisogno di chiamare OpenFile
direttamente.
Avviso relativo al trattamento di EOF. Leggi
tenta di riempire buf
su ogni chiamata e restituisce io.EOF
come errore se raggiunge la fine del file. In questo caso buf
manterrà comunque i dati. Le chiamate successive a Leggi
restituiscono zero come il numero di byte letti e lo stesso io.EOF
come errore. Qualsiasi altro errore provocherà un panico.
Uso di bufio
package main
import (
"bufio"
"io"
"os"
)
func main() {
// open input file
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// close fi on exit and check for its returned error
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()
// make a read buffer
r := bufio.NewReader(fi)
// open output file
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
// make a write buffer
w := bufio.NewWriter(fo)
// make a buffer to keep chunks that are read
buf := make([]byte, 1024)
for {
// read a chunk
n, err := r.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}
// write a chunk
if _, err := w.Write(buf[:n]); err != nil {
panic(err)
}
}
if err = w.Flush(); err != nil {
panic(err)
}
}
bufio
agisce solo come buffer qui, perché non abbiamo molto a che fare con i dati. Nella maggior parte delle altre situazioni (specialmente con file di testo) bufio
è molto utile dandoci una bella API per leggere e scrivere in modo semplice e flessibile, mentre gestisce il buffering dietro le quinte.
Uso di ioutil
package main
import (
"io/ioutil"
)
func main() {
// read the whole file at once
b, err := ioutil.ReadFile("input.txt")
if err != nil {
panic(err)
}
// write the whole body at once
err = ioutil.WriteFile("output.txt", b, 0644)
if err != nil {
panic(err)
}
}
Facile come una torta! Ma usalo solo se sei sicuro di non avere a che fare con file di grandi dimensioni.
Altri suggerimenti
Questa è una buona versione:
package main
import (
"io/ioutil";
)
func main() {
contents,_ := ioutil.ReadFile("plikTekstowy.txt")
println(string(contents))
ioutil.WriteFile("filename", contents, 0644)
}
Uso di io.Copy
package main
import (
"io"
"log"
"os"
)
func main () {
// open files r and w
r, err := os.Open("input.txt")
if err != nil {
panic(err)
}
defer r.Close()
w, err := os.Create("output.txt")
if err != nil {
panic(err)
}
defer w.Close()
// do the actual work
n, err := io.Copy(w, r)
if err != nil {
panic(err)
}
log.Printf("Copied %v bytes\n", n)
}
Se non hai voglia di reinventare la ruota, io.Copy
e io.CopyN
potrebbero esserti utili. Se controlli l'origine della funzione io.Copy, è nient'altro che una delle soluzioni di Mostafa (quella "di base", in realtà) confezionata nella libreria Go. Stanno usando un buffer significativamente più grande di lui, però.
[] byte
è una sezione (simile a una sottostringa) di tutto o parte di un array di byte. Pensa alla sezione come una struttura di valori con un campo puntatore nascosto per consentire al sistema di individuare e accedere a tutto o parte di un array (la sezione), oltre ai campi per la lunghezza e la capacità della sezione, a cui puoi accedere utilizzando il cap ()
.
Ecco uno starter kit funzionante per te, che legge e stampa un file binario; dovrai modificare il valore letterale inName
per fare riferimento a un piccolo file sul tuo sistema.
package main
import (
"fmt";
"os";
)
func main()
{
inName := "file-rw.bin";
inPerm := 0666;
inFile, inErr := os.Open(inName, os.O_RDONLY, inPerm);
if inErr == nil {
inBufLen := 16;
inBuf := make([]byte, inBufLen);
n, inErr := inFile.Read(inBuf);
for inErr == nil {
fmt.Println(n, inBuf[0:n]);
n, inErr = inFile.Read(inBuf);
}
}
inErr = inFile.Close();
}
Con le versioni Go più recenti, leggere / scrivere su / da file è facile. Per leggere da un file:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile("text.txt")
if err != nil {
return
}
fmt.Println(string(data))
}
Per scrivere su un file:
package main
import "os"
func main() {
file, err := os.Create("text.txt")
if err != nil {
return
}
defer file.Close()
file.WriteString("test\nhello")
}
Questo sovrascriverà il contenuto di un file (crea un nuovo file se non fosse presente).
Prova questo:
package main
import (
"io";
)
func main() {
contents,_ := io.ReadFile("filename");
println(string(contents));
io.WriteFile("filename", contents, 0644);
}
Solo guardando la documentazione sembra che dovresti semplicemente dichiarare un buffer di tipo [] byte e passarlo alla lettura che leggerà fino a quel numero di caratteri e restituirà il numero di caratteri effettivamente letti (e un errore).
I documenti dicono
Lettura legge fino a len (b) byte dal file. Restituisce il numero di byte letti e un eventuale Errore. EOF è segnalato da un conteggio zero con err impostato su EOF.
Non funziona?
EDIT: Inoltre, penso che dovresti forse usare le interfacce Reader / Writer dichiarate nel pacchetto bufio invece di usare il pacchetto os .
Il metodo Read accetta un parametro byte perché è il buffer in cui leggerà. È un linguaggio comune in alcuni ambienti e ha un senso quando ci pensi.
In questo modo è possibile determinare quanti byte verranno letti dal lettore e controllare il ritorno per vedere quanti byte sono stati effettivamente letti e gestire gli errori in modo appropriato.
Come altri hanno sottolineato nelle loro risposte, bufio è probabilmente quello che vuoi per leggere dalla maggior parte dei file.
Aggiungerò un altro suggerimento poiché è davvero utile. La lettura di una riga da un file si ottiene meglio non con il metodo ReadLine ma con il metodo ReadBytes o ReadString.