Как читать/писать из/в файл с помощью Go?

Я пытался учиться самостоятельно, но я был в шоке от попыток чтения и записи в обычные файлы.

Я могу дойти до inFile, _ := os.Open(INFILE, 0, 0), но на самом деле получение содержимого файла не имеет смысла, потому что функция чтения принимает []byte в качестве параметра.

func (file *File) Read(b []byte) (n int, err Error)

Ответ 1

Позвольте составить Go-совместимый список всех способов чтения и записи файлов в Go.

Поскольку API файлов недавно изменился, и большинство других ответов не работают с Go 1. Они также пропускают bufio, что важно IMHO.

В следующих примерах я копирую файл, читая его и записывая в целевой файл.

Начните с основ

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

Здесь я использовал os.Open и os.Create, которые являются удобными обертками вокруг os.OpenFile. Обычно нам не нужно напрямую звонить OpenFile.

Обратите внимание на обработку EOF. Read пытается заполнить buf для каждого вызова и возвращает io.EOF как ошибку, если он достигает конца файла при этом. В этом случае buf будет хранить данные. Последующие вызовы Read возвращают ноль в качестве количества прочитанных байтов и того же io.EOF, что и ошибка. Любая другая ошибка приведет к панике.

Использование 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 просто действует как буфер здесь, потому что мы не имеем ничего общего с данными. В большинстве других ситуаций (особенно с текстовыми файлами) bufio очень полезно, предоставляя нам хороший API для чтения и записи легко и гибко, в то время как он обрабатывает буферизацию за кулисами.

Использование 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)
    }
}

Легко, как пирог! Но используйте его, только если вы уверены, что не имеете дело с большими файлами.

Ответ 2

Это хорошая версия:

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}

Ответ 3

Использование 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)
}

Если вы не хотите изобретать колесо, io.Copy и io.CopyN могут служить вам хорошо. Если вы проверьте источник функции io.Copy, это не что иное, как одно из решений Mostafa (фактически "базовое" ), упакованное в библиотеку Go. Они используют значительно больший буфер, чем он.

Ответ 4

[]byte - это срез (подобный подстроке) всего или части массива байтов. Подумайте об срезе как структуре значений со скрытым полем указателя, чтобы система могла находить и получать доступ ко всем или части массива (срез) плюс поля для длины и емкости среза, к которым вы можете получить доступ, используя len() и cap().

Здесь рабочий комплект для вас, который читает и печатает двоичный файл; вам нужно будет изменить значение литерала inName, чтобы ссылаться на маленький файл в вашей системе.

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

Ответ 5

Попробуйте следующее:

package main

import (
  "io"; 
  )


func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0x644);
}

Ответ 6

Какие новые версии Go, чтение/запись в/из файла легко. Для чтения из файла:

package main

import (
    "fmt"
    "io/ioutil"
)

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

Для записи в файл:

package main

import "os"

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

    file.WriteString("test\nhello")
}

Это перезапишет содержимое файла (создайте новый файл, если он там не был).

Ответ 7

Просто взглянув на документацию, вам следует просто объявить буфер типа [] byte и передать его для чтения, который затем будет считывать до этого количества символов и возвращать количество фактически прочитанных символов (и ошибку).

Документы говорят

Чтение считывает до len (b) байтов из файла. Он возвращает количество прочитанных байтов и ошибку, если таковые имеются. EOF сигнализируется нулевым отсчетом с ошибкой, установленной в EOF.

Не работает ли это?

EDIT: Кроме того, я думаю, вы должны использовать интерфейсы Reader/Writer, объявленные в пакете bufio, вместо использования пакета os.

Ответ 8

Метод Read принимает байтовый параметр, потому что это буфер, в который он будет считываться. Это обычная идиома в некоторых кругах и имеет смысл, когда вы об этом думаете.

Таким образом вы можете определить, сколько байтов будет прочитано читателем, и проверит возврат, чтобы узнать, сколько байтов действительно было прочитано и надлежащим образом обрабатывать любые ошибки.

Как указывали другие пользователи в своих ответах, bufio, вероятно, вы хотите читать из большинства файлов.

Я добавлю еще один намек, так как это действительно полезно. Чтение строки из файла лучше всего выполнить не методом ReadLine, а методом ReadBytes или ReadString.