Go в примери: Четене от файлове

Писането във файлове и четенето от тях са прости задачи, нужни в много програми. Първо ще разгледаме някои примери за четене от файлове.

package main
import (
    "bufio"
    "fmt"
    "io"
    "os"
    "path/filepath"
)

Четенето от файл изисква проверка за грешка почти при всяко извикване на функция. Този помощник ще опрости проверките.

func check(e error) {
    if e != nil {
        panic(e)
    }
}
func main() {

Може би най-простата задача е изчитането на цялото съдържание от файла в паметта наведнъж.

    пѫтека := filepath.Join(os.TempDir(), "dat")
    dat, err := os.ReadFile(пѫтека)
    check(err)
    fmt.Print(string(dat))

Често ще ви се налага да управлявате начина и да избирате кои части от файла да бъдат прочетени. В тези случаи използвайте първо os.Open(пѫтека). Тази функция връща стойности от видовете (*os.File, error).

    f, err := os.Open(пѫтека)
    check(err)

Прочитаме няколко байта1 от началото на файла. Разрешаваме да бъдат прочетени 6, но забележете също колко всъщност са прочетени.(бел. прев. какво иска да каже авторът с „но заб…”???) За добавяне като глава обяснение след примера: http://www.tcpipguide.com/free/t_BinaryInformationandRepresentationBitsBytesNibbles.htm


  1. byte наистина означава хапка защото понятието е просто написана по различен начин думата bite, за да няма объркване с въведеното вече понятие за най-малката възможна единица данни bit. Виж https://en.wikipedia.org/wiki/Byte#citenote-Buchholz1977-14
    b1 := make([]byte, 6)
    n1, err := f.Read(b1)
    check(err)
    fmt.Printf("%d хапки: %s\n", n1, string(b1[:n1]))

Също така можете да „скочите” със Seek до познато място във файла и да четете с Read оттам нататък.

    o2, err := f.Seek(6, io.SeekStart)
    check(err)

Прочитаме новия ред и два байта – общо три.

    b2 := make([]byte, 3)
    n2, err := f.Read(b2)
    check(err)
    fmt.Printf("%d хапки @ %d: ", n2, o2)
    fmt.Printf("%v\n", string(b2[:n2]))

Други начини за търсене във файл са чрез отнасяне към текущото място на показалеца.

    _, err = f.Seek(2, io.SeekCurrent)
    check(err)

и отнесено към края на файла

    _, err = f.Seek(-4, io.SeekEnd)
    check(err)

Пакетът io предоставя някои полезни за четене на файлове функции. Например четения като горните могат да бъдат осъществени по-мощно с помощта на ReadAtLeast.

    o3, err := f.Seek(6, io.SeekStart)
    check(err)
    b3 := make([]byte, 3)
    n3, err := io.ReadAtLeast(f, b3, 3)
    check(err)
    fmt.Printf("%d хапки @ %d: %s\n", n3, o3, string(b3))

Няма готова функция за връщане в началото но Seek(0, io.SeekStart) върши точно това.

    _, err = f.Seek(0, io.SeekStart)
    check(err)

Пакетът bufio осъществява складиращ четец, който може да ни е от полза както заради производителността си така и за това, че предоставя допълнителни методи за четене.

    r4 := bufio.NewReader(f)
    b4, err := r4.Peek(9)
    check(err)
    fmt.Printf("9 хапки: %s\n", string(b4))

Затваряме файла, като сме готови. Обикновено това действие бива насрочено веднага след извикването на Open(…) с помощта на defer.

    f.Close()
}

Важно! За да работят примерите, трябва пѫтят, описан чрез пѫтека да сочи до съществуващ файл, така че създайте го, например така:

$ echo "ала" > /tmp/dat
$ echo "баланица" >> /tmp/dat

Забележете, че внимаваме колко байта ще прочетем, защото българските букви, поради местонахожденията си в таблицата УНИКОД, чиито номера не могат да се вместят в един байт, са представени в UTF-8 чрез двубайтови последователности. Иначе просто ще имаме счупени букви. Проверете като промените броя байтове при някои четения..

$ go run reading-files.go 
ала
баланица
6 хапки: ала
3 хапки @ 6: 
б
3 хапки @ 6: 
б
9 хапки: ала
б

А сега ще разгледаме писането във файлове.

Следващ пример: .