Jak odczytywać/zapisywać Z / do pliku za pomocą Go?

Próbowałem nauczyć się Go na własną rękę, ale zostałem zatrzymany na próbie odczytu i zapisu do zwykłych plików.

Mogę dotrzeć do inFile, _ := os.Open(INFILE, 0, 0), ale w rzeczywistości uzyskanie zawartości pliku nie ma sensu, ponieważ funkcja read przyjmuje []byte jako parametr.

func (file *File) Read(b []byte) (n int, err Error)
 239
Author: John Weldon, 2009-11-30

8 answers

Stwórzmy kompatybilną z Go listę wszystkich sposobów odczytu i zapisu plików w Go.

Ponieważ API plików ostatnio się zmieniło i większość innych odpowiedzi nie działa z Go 1. Brakuje im też bufio, co jest ważne IMHO.

W poniższych przykładach kopiuję plik przez odczyt z niego i zapis 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 owijkami wokół os.OpenFile. Zazwyczaj nie musisz zadzwonić OpenFile bezpośrednio.

Uwaga dotycząca EOF. Read próbuje wypełnić buf przy każdym wywołaniu i zwraca io.EOF jako błąd, jeśli dojdzie do końca pliku w ten sposób. W tym przypadku buf nadal będzie przechowywać dane. Kolejne wywołania Read zwracają zero jako liczbę odczytanych bajtów i tę samą io.EOF jako 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 tu tylko jako bufor, ponieważ nie mamy wiele wspólnego z danymi. W większości innych situations (szczególnie z plikami tekstowymi) bufio jest bardzo przydatne, ponieważ daje nam ładne API do łatwego i elastycznego czytania i pisania, podczas gdy obsługuje buforowanie za kulisami.

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)
    }
}

Łatwe jak ciasto! Ale używaj go tylko wtedy, gdy jesteś pewien, że nie masz do czynienia z dużymi plikami.

 413
Author: Mostafa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-14 23:02:00

To jest dobra Wersja:

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}
 41
Author: Piotr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-11 01:43:56

Za pomocą 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)
}

Jeśli nie masz ochoty odkrywać koła na nowo, io.Copy i io.CopyN mogą ci dobrze służyć. Jeśli sprawdzisz źródło io.Funkcja kopiowania, jest to nic innego jak jedno z najbardziej zaawansowanych rozwiązań (w zasadzie "podstawowe") spakowanych w bibliotece Go. Używają jednak znacznie większego bufora niż on.

 25
Author: user7610,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-23 20:46:16

[]byte jest wycinkiem (podobnym do podłańcucha) całości lub części tablicy bajtów. Potraktuj plasterek jako strukturę wartości z ukrytym polem wskaźnika, aby system mógł zlokalizować i uzyskać dostęp do całości lub części tablicy (plasterka) oraz pól długości i pojemności plasterka, do których można uzyskać dostęp za pomocą funkcji len() i cap().

Oto działający zestaw startowy dla Ciebie, który odczytuje i drukuje plik binarny; musisz zmienić wartość inName literalną, aby odnieść się do małego pliku na Twój system.

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();
}
 10
Author: peterSO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-04 03:15:21

Spróbuj tego:

package main

import (
  "io"; 
  )


func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0644);
}
 6
Author: marketer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-26 22:58:48

Które nowe wersje Go, Odczyt/Zapis do / Z pliku jest łatwy. Do odczytania z pliku:

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    data, err := ioutil.ReadFile("text.txt")
    if err != nil {
        return
    }
    fmt.Println(string(data))
}

Zapis do pliku:

package main

import "os"

func main() {
    file, err := os.Create("text.txt")
    if err != nil {
        return
    }
    defer file.Close()

    file.WriteString("test\nhello")
}

To nadpisze zawartość pliku(utwórz nowy plik, jeśli go nie ma).

 5
Author: Salvador Dali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-23 03:38:44

Po prostu patrząc na dokumentację wydaje się, że powinieneś zadeklarować bufor typu []byte i przekazać go do odczytu, który następnie odczyta do tylu znaków i zwróci liczbę faktycznie odczytanych znaków (i błąd).

Docs say

Odczyt odczytuje do len (b) bajtów z pliku. Zwraca liczbę odczytanych bajtów i błąd, jeśli taki istnieje. EOF jest sygnalizowany liczbą zerową z err ustawioną na EOF.

Czy to nie działa?

Edytuj: Myślę też, że powinieneś używać interfejsów Reader/Writer zadeklarowanych w pakiecie bufio zamiast os .

 1
Author: Hannes Ovrén,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-30 19:22:41

Metoda Read pobiera parametr bajtowy, ponieważ jest to bufor, do którego będzie odczytywana. Jest to popularny Idiom w niektórych kręgach i ma jakiś sens, gdy się o tym pomyśli.

W ten sposób można określić, ile bajtów zostanie odczytanych przez czytnik i sprawdzić Powrót, aby zobaczyć, ile bajtów rzeczywiście zostało odczytanych i odpowiednio obsłużyć wszelkie błędy.

Jak inni wskazywali w swoich odpowiedziach bufio jest prawdopodobnie tym, co chcesz czytać z większości plików.

Dodam jeszcze jedną podpowiedź, ponieważ jest to naprawdę przydatne. Odczyt linii z pliku najlepiej wykonać Nie metodą ReadLine, lecz metodą ReadBytes lub ReadString.

 1
Author: Jeremy Wall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-19 16:22:33