Zróbmy listę kompatybilną z Go 1 wszystkich sposobów odczytu i zapisu plików w Go.
Ponieważ interfejs API plików ostatnio się zmienił i większość innych odpowiedzi nie działa z Go 1. Oni również tęsknią bufio
ważnego IMHO.
W poniższych przykładach kopiuję plik, czytając go i zapisując do pliku docelowego.
Zacznij od podstaw
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)
}
}
}
Tutaj użyłem os.Open
i os.Create
które są wygodnymi opakowaniami os.OpenFile
. Zwykle nie musimy dzwonić OpenFile
bezpośrednio.
Zwróć uwagę na leczenie EOF. Read
próbuje wypełnić buf
każde wywołanie i zwraca io.EOF
jako błąd, jeśli do tego dojdzie do końca pliku. W takim przypadku buf
nadal będą przechowywane dane. Kolejne wywołania Read
zwracają zero jako liczbę odczytanych bajtów i taką samą io.EOF
jak błąd. Każdy inny błąd doprowadzi do paniki.
Za pomocą 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
działa tutaj tylko jako bufor, ponieważ nie mamy wiele wspólnego z danymi. W większości innych sytuacji (szczególnie z plikami tekstowymi) bufio
jest bardzo przydatny, ponieważ zapewnia nam przyjemny interfejs API do łatwego i elastycznego czytania i pisania, a także obsługuje buforowanie za sceną.
Za pomocą 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)
}
}
Proste jak ciasto! Ale używaj go tylko wtedy, gdy masz pewność, że nie masz do czynienia z dużymi plikami.
panic("error in writing")
) nie jest konieczne.To jest dobra wersja:
źródło
0x777
jest fałszywy. W każdym przypadku powinno być więcej jak0644
lub0755
(ósemkowy, a nie hex).Za pomocą
io.Copy
Jeśli nie masz ochoty wymyślać koła na nowo,
io.Copy
iio.CopyN
może ci dobrze służyć. Jeśli sprawdzisz źródło funkcji io.Copy, jest to jedno z rozwiązań Mostafa (właściwie „podstawowe”) spakowane w bibliotece Go. Jednak używają znacznie większego bufora niż on.źródło
w.Sync()
poio.Copy(w, r)
io.Copy()
zapisze tylko dane, którymi go karmisz, więc jeśli istniejący plik zawiera więcej treści, nie zostanie usunięty, co może spowodować uszkodzenie pliku.w, err := os.Create("output.txt")
, to, co opisujesz, nie nastąpi, ponieważ „Utwórz tworzy lub obcina nazwany plik. Jeśli plik już istnieje, jest obcięty”. golang.org/pkg/os/#Create .Dzięki nowszym wersjom Go odczyt / zapis do / z pliku jest łatwy. Aby odczytać z pliku:
Aby zapisać do pliku:
Spowoduje to zastąpienie zawartości pliku (utwórz nowy plik, jeśli go nie było).
źródło
[]byte
jest wycinkiem (podobnym do podłańcucha) całości lub części tablicy bajtów. Pomyśl o wycinku jako strukturze wartości z ukrytym polem wskaźnika, aby system mógł zlokalizować i uzyskać dostęp do całości lub części tablicy (wycinka), a także pola dotyczące długości i pojemności wycinka, do których można uzyskać dostęp za pomocą funkcjilen()
icap()
.Oto działający zestaw startowy, który odczytuje i drukuje plik binarny; musisz zmienić
inName
wartość literału, aby odnosić się do małego pliku w systemie.źródło
if
blokiemSpróbuj tego:
źródło
Patrząc na dokumentację, wydaje się, że powinieneś po prostu zadeklarować bufor typu [] bajt i przekazać go do odczytu, który następnie odczyta aż tyle znaków i zwróci liczbę faktycznie odczytanych znaków (i błąd).
Doktorzy mówią
Czy to nie działa?
EDYCJA: Myślę też, że powinieneś raczej użyć interfejsów Reader / Writer zadeklarowanych w pakiecie bufio zamiast używać pakietu os .
źródło
Metoda Read pobiera parametr bajtowy, ponieważ jest to bufor, w którym będzie czytać. Jest to powszechny idiom w niektórych kręgach i ma sens, gdy się nad nim zastanowić.
W ten sposób możesz określić, ile bajtów zostanie odczytanych przez czytnik i sprawdzić zwrot, aby zobaczyć, ile bajtów faktycznie odczytano, i odpowiednio obsłużyć wszelkie błędy.
Jak wskazali inni w swoich odpowiedziach, bufio jest prawdopodobnie tym, czego chcesz czytać z większości plików.
Dodam jeszcze jedną wskazówkę, ponieważ jest naprawdę przydatna. Odczyt wiersza z pliku najlepiej osiągnąć nie metodą ReadLine, ale metodą ReadBytes lub ReadString.
źródło