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. @see{error handling}

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