[TOC]

Podstawowe operacje na plikach

W bibliotece standardowej nie ma zbyt wielu funkcji skrótowych do pracy na plikach, ale same "niskie" funkcje nie są szczególnie skomplikowane i czasem warto mieć pod kontrolą każdy aspekt otwierania pliku. Przybliżę podstawowe operacje na plikach, które możesz replikować gdzie ci się żywnie podoba.

Podstawowe operacje na plikach to:

  • tworzenie plików
  • otwieranie plików
  • czytanie zawartości plików
  • pisanie do pliku
  • zamykanie pliku

Otwieranie i czytanie plików

Do zwykłego wczytania całej zawartości pliku polecam io/ioutils.ReadFile. Jeśli potrzebujesz wczytywać fragmenty, to lepiej sprawdzi się os.Open. Wszystkie możliwe operacje na plikach można wykonać za pomocą os.OpenFile, której zastosowanie znajdziesz w ostatniej sekcji.

io/ioutils.ReadFile

Link do specyfikacji.

Sygnatura func ReadFile(filename string) ([]byte, os.Error), wymaga podania nazwy pliku (jego ścieżki), a zwraca wycinek bajtów i wartość spełniającą interfejs os.Error.

Jako przykład zastosowania niech służy program czytający zawartość pliku i wyprowadzający go na ekran

package main
import (
    "io/ioutil"
    "os"
    "fmt"
)

func main() {
    arguments := os.Args
    if len(arguments) < 2 {
        fmt.Println("Podaj nazwę pliku który chcesz wczytać")
        os.Exit(1)
    }
    filename := arguments[1]
    fcontents, err := ioutil.ReadFile(filename)
    if err != nil {
        fmt.Printf("Błąd wczytywania pliku %v \n", filename)
        os.Exit(1)
    }
    os.Stdout.Write(fcontents)
    //dodajmy kończącą nową linię
    os.Stdout.Write([]byte("\n"))

}

os.Open

os.Open zwróci wartość typu *os.File, dzięki której możemy manipulować danymi pliku, zaś on sam zostanie otwarty tylko do odczytu.

Jako przykład napiszemy funkcję, która zwraca wartość []string, której elementy odpowiadają pojedynczym liniom pliku tekstowego.

func ReadLines(filename string) (lines []string, err os.Error) {
    file, err := os.Open(filename)
    if err != nil {
        return
    }
    reader := bufio.NewReader(file)
    for bline, e := reader.ReadBytes('\n'); e == nil; bline, e = reader.ReadBytes('\n') {
        lines = append(lines, string(bline))
    }
    return
}

Tworzenie i zapisywanie plików

Plik można stworzyć za pośrednictwem czterech różniących się funkcji.

os.Create

Link do specyfikacji

Sygnatura funkcji func Create(name string) (file *File, err Error), mówi nam że parametrem ma być nazwa pliku (ścieżka do pliku), a zwrócone zostaną wskaźnik na wartość typu os.File i wartość spełniająca interfejs os.Error.

Wywołanie tej funkcji zaowocuje stworzeniem pliku i ustawieniem jego praw na 0666, czyli do odczytu i zapisu dla wszystkich. W przypadku gdy wskazany plik już istniał zostanie on opróżniony. os.Create zapewnia nas, że plik będzie pusty i gotowy do zapisu i ewentualnego odczytu.

Prosty przykład, w którym utworzymy plik, zapiszemy w nim ciąg znaków i wczytamy jego zawartość i wyświetlimy na ekranie: main.go

package main
import (
    "fmt"
    "os"
)

func main() {
    contents := "Zawartość pliku otworzonego funkcją os.Create"
    filename := "oscreate.test"

    file, err := os.Create()
    defer file.Close()//zamknij plik tuż po wyjściu z funkcji
    if err != nil {
        fmt.Println("Błąd tworzenia pliku", err)
        os.Exit(1)
    }

    _, werr := file.Write([]byte(contents))
    if werr != nil {
        fmt.Println("Błąd zapisywania do pliku", werr)
        os.Exit(1)
    }

    _, serr := file.Seek(0, 0) //przesuwamy wewnętrzny kursor pliku na jego początek

    if serr != nil {
        fmt.Println("Błąd przesuwania kursora pliku", werr)
        os.Exit(1)
    }

    buf := make([]byte, len(contents) + 1)
    file.Read(buf)
    fmt.Println(string(buf))
    os.Exit(0)
}

io/ioutil.WriteFile

ioutil.WriteFile to funkcja przeznaczona tylko i wyłącznie do zapisywania określonego bufora danych do pliku, jej przewaga nad os.Create, polega na tym że można określić jaki ma być poziom dostępu do pliku, no i nie trzeba operować na obiekcie typu *os.File.

Sygnatura tej funkcji wygląda następująco: func WriteFile(filename string, data []byte, perm uint32) os.Error. Jako parametry przyjmuje nazwę (ścieżkę), ciąg bajtów do zapisania i poziom uprawnień do tworzonego pliku. Zwraca wartość typu os.Error, która jest nilem jeśli nie było żadnego, błędu.

    Przykład będzie skromniejszy niż poprzedni, gdyż nie możemy manipulować wartością typu os.File, bo jej nie otrzymujemy.

    package main
    import (
        "fmt"
        "io/ioutil"
        "os"
    )
    
    func main() {
        contents := "Zawartość pliku otworzonego funkcją ioutil.WriteFile"
        filename := "oscreate.test"
        err := ioutil.WriteFile(filename , []byte(contents), 0666)
        if err != nil {
            fmt.Println("Błąd tworzenia pliku", err)
            os.Exit(1)
        }
        fmt.Println("wszystko poszło jak należy")
        os.Exit(0)
    }

    io/ioutil.TempFile

    Link do specyfikacji

    Ta funkcja pozwoli nam na utworzenie pliku tymczasowego o unikalnej nazwie we wskazanym katalogu. Sygnatura func TempFile(dir, prefix string) (f *os.File, err os.Error), czyli musimy wskazać katalog docelowy i prefix nazwy pliku. Funkcja sama dobierze unikalną nazwę którą możemy uzyskać wywołując metodę f.Name() na wartości typu os.File.

    io/ioutil.TempFile nie dostarcza mechanizmów do automatycznego usuwania plików tymczasowych, program sam musi zadbać o sprzątanie po sobie

    Przykład:

    package main
    import (
        "fmt"
        "io/ioutil"
        "os"
    )
    
    func main() {
        contents := "Zawartość pliku otworzonego funkcją ioutil.WriteFile"
        fileprefix := "tmpfile"
        dir := "./"
        file, err := ioutil.TempFile(dir, filename)
        if err != nil {
            fmt.Println("Błąd tworzenia pliku", err)
            os.Exit(1)
        }
        fmt.Println("Plik utworzony i otwarty")
        file.Write([]byte(contents))
        os.Exit(0)
    }

    os.OpenFile

    To najbardziej ogólna z funkcji otwierających plik, dzięki odpowiedniemu doborowi przełączników możemy także tworzyć nowe pliki. We wcześniejszych przykładach wszystkie funkcje, albo tworzyły puste pliki albo czyściły już istniejące w trakcie wywołania. Pokażę jak otwierać i tworzyć pliki do których chcemy dopisywać dane, przydatne przy pisaniu logów.

    package main
    import (
        "os"
        "fmt"
    )
    
    func OpenAppendOnlyFile(name string) (*os.File, os.Error) {
        openflags :=  os.O_APPEND|os.O_CREATE|os.O_WRONLY
        fileperm := 0755
        return os.OpenFile(name, openflags, fileperm )
    }
    
    func main() {
        file, err := OpenAppendOnlyFile("new.log")
        if err != nil {
            fmt.Println("Błąd otwierania pliku", err)
            os.Exit(1)
        }
        n, err := file.Write([]byte("jeden\n"))
        if err != nil {
            fmt.Println("Błąd zapisu", err)
            os.Exit(1)
        }
        fmt.Println("zapisano", n, "bytesów");
        defer file.Close()
    }