Как читать / писать из / в файл с помощью Go? - PullRequest
266 голосов
/ 30 ноября 2009

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

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

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

Ответы [ 8 ]

447 голосов
/ 16 марта 2012

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

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

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

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

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

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

45 голосов
/ 11 апреля 2010

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

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}
26 голосов
/ 24 октября 2012

Использование 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. Они используют значительно больший буфер, чем он.

10 голосов
/ 01 декабря 2009

[]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();
}
8 голосов
/ 23 ноября 2015

В новых версиях 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 голосов
/ 30 ноября 2009

Попробуйте это:

package main

import (
  "io"; 
  )


func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0644);
}
1 голос
/ 19 марта 2012

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

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

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

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

1 голос
/ 30 ноября 2009

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

Документы скажем

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

Разве это не работает?

РЕДАКТИРОВАТЬ: Кроме того, я думаю, что вам, возможно, следует использовать интерфейсы Reader / Writer, объявленные в пакете bufio вместо использования os .

...