361

I've been trying to learn Go on my own, but I've been stumped on trying read from and write to ordinary files.

I can get as far as inFile, _ := os.Open(INFILE, 0, 0), but actually getting the content of the file doesn't make sense, because the read function takes a []byte as a parameter.

func (file *File) Read(b []byte) (n int, err Error)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Seth Hoenig
  • 7,047
  • 6
  • 31
  • 30

10 Answers10

563

Let's make a Go 1-compatible list of all the ways to read and write files in Go.

Because file API has changed recently and most other answers don't work with Go 1. They also miss bufio which is important IMHO.

In the following examples I copy a file by reading from it and writing to the destination file.

Start with the basics

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

Here I used os.Open and os.Create which are convenient wrappers around os.OpenFile. We usually don't need to call OpenFile directly.

Notice treating EOF. Read tries to fill buf on each call, and returns io.EOF as error if it reaches end of file in doing so. In this case buf will still hold data. Consequent calls to Read returns zero as the number of bytes read and same io.EOF as error. Any other error will lead to a panic.

Using 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 is just acting as a buffer here, because we don't have much to do with data. In most other situations (specially with text files) bufio is very useful by giving us a nice API for reading and writing easily and flexibly, while it handles buffering behind the scenes.


Note: The following code is for older Go versions (Go 1.15 and before). Things have changed (ioutil is deprecated since Go 1.16). For the new way, take a look at this answer.

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

Easy as pie! But use it only if you're sure you're not dealing with big files.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Mostafa
  • 26,886
  • 10
  • 57
  • 52
  • 71
    For anyone who stumbles upon this question, it was originally asked in 2009 before these libraries were introduced, so please, use this answer as your guide! – Seth Hoenig May 17 '12 at 02:12
  • 2
    According to http://golang.org/pkg/os/#File.Write, when Write hasn't written all bytes, it returns an error. So the extra check in the first example (`panic("error in writing")`) isn't necessary. – ayke Jan 05 '13 at 13:47
  • 17
    Note that these examples aren't checking the error return from fo.Close(). From the Linux man pages close(2): Not checking the return value of close() is a common but nevertheless serious programming error. It is quite possible that errors on a previous write(2) operation are first reported at the final close(). Not checking the return value when closing the file may lead to silent loss of data. This can especially be observed with NFS and with disk quota. – Nick Craig-Wood Jan 25 '13 at 07:12
  • @ayke Nice catch. I updated the answer (and some of my own code!) and removed the useless error check. – Mostafa Mar 15 '13 at 19:09
  • 1
    @NickCraig-Wood Thank your for your important tip. I added extra code to check for error on calling `Close`. I’m also starting to check for error on `Close` in my own programs. – Mostafa Mar 15 '13 at 19:11
  • With the bufio example, the compiler complains that n2 is declared but not used. – cdf Apr 03 '13 at 14:17
  • @cdf Sorry for the bug. It was introduced in the last edit. Updated the answer to fix this. – Mostafa Apr 04 '13 at 10:45
  • Learning go here too. Shouldn't the defer code be the following: defer func() { if err := file.close(); err != nil { panic(err) } }() – chmike Apr 20 '13 at 09:58
  • My above error handling of close make only sense when one is writing to the file. When the file is opened for read only access, defer file.close() is all we need. – chmike Apr 20 '13 at 10:05
  • @chmike Thanks for catching that `err` bug. Fixed it. I was not sure about closing read-only files. Do you have any reference that says we should not worry about close errors for read-only files? – Mostafa Apr 20 '13 at 11:05
  • I deduced it from @NickCraig-Wood's comment, but indeed we should verify it every time to write bullet proof code. – chmike Apr 21 '13 at 17:27
  • 15
    So, what's a "big" file? 1KB? 1MB? 1GB? Or does "big" depend on the machine's hardware? – 425nesp Dec 10 '14 at 00:27
  • An important difference between os.File.Read() and bufio.Reader.Read() is when they can return fewer bytes than requested. The former will always try to fill the buffer, and only fille it with less bytes when it hits the EOF. While the latter, may fill with less bytes than the []byte can hold, and still not be serving up an EOF err yet. – DragonFax Mar 21 '15 at 19:30
  • 4
    @425nesp It reads whole the file into the memory, so it depends on the amount of available memory in the running machine. – Mostafa May 09 '15 at 07:31
  • What is "0644" in the WriteFile call? – ebrts Nov 24 '15 at 03:28
  • 1
    @ebrts It defines the permissions of the file. You can read about it here: https://en.wikipedia.org/wiki/File_system_permissions – Mostafa Nov 25 '15 at 16:34
  • 1
    You should probably `break` on `io.EOF` explicitly, since - according to the documentation - `n==0` doesn't always signify end of file From the `Reader` interface source: `// Implementations of Read are discouraged from returning a // zero byte count with a nil error, except when len(p) == 0. // Callers should treat a return of 0 and nil as indicating that // nothing happened; in particular it does not indicate EOF.` – Jean Spector Jul 14 '20 at 09:57
  • Isn't closing a file makes it flush the data? `w.Flush(...)` looks useless here. – x-yuri Feb 04 '23 at 04:02
  • @NickCraig-Wood SO what do you do if and when ```File.Close``` returns an error? Do you delete the file? What it that fails? – TheRealChx101 Feb 08 '23 at 09:36
  • Should be noted that `ReadFile` and `WriteFile` are now part of the `os` package. – Michael Bonnet Aug 23 '23 at 17:22
57

This is a good version (but do note that ioutil is deprecated since Go 1.16):

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}
x-yuri
  • 16,722
  • 15
  • 114
  • 161
Piotr
  • 595
  • 4
  • 2
44

New Way

Starting with Go 1.16, use os.ReadFile to load the file into memory, and use os.WriteFile to write to a file from memory (ioutil.ReadFile now calls os.ReadFile and is deprecated).

Be careful with the os.ReadFile because it reads the whole file into memory.

package main

import "os"
import "log"

func main() {
    b, err := os.ReadFile("input.txt")
    if err != nil {
        log.Fatal(err)
    }

    // `b` contains everything your file has.
    // This writes it to the Standard Out.
    os.Stdout.Write(b)

    // You can also write it to a file as a whole.
    err = os.WriteFile("destination.txt", b, 0644)
    if err != nil {
        log.Fatal(err)
    }
}
Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
Inanc Gumus
  • 25,195
  • 9
  • 85
  • 101
42

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

If you don't feel like reinventing the wheel, the io.Copy and io.CopyN may serve you well. If you check the source of the io.Copy function, it is nothing but one of the Mostafa's solutions (the 'basic' one, actually) packaged in the Go library. They are using a significantly larger buffer than he is, though.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
user7610
  • 25,267
  • 15
  • 124
  • 150
  • 6
    one thing worth to mention - to be sure that the content of the file was written to disk, you need to use `w.Sync()` after the `io.Copy(w, r)` – Shay Tsadok Jul 26 '16 at 22:07
  • Also, if you write to already existing file, `io.Copy()` will only write the data you feed it with, so if existing file had more content, it won't be removed, which may result in corrupted file. – Invidian Feb 26 '20 at 13:25
  • 4
    @Invidian It all depends on how you open the destination file. If you do `w, err := os.Create("output.txt")`, what you describe does not happen, because "Create creates or truncates the named file. If the file already exists, it is truncated." https://golang.org/pkg/os/#Create. – user7610 Mar 01 '20 at 10:10
  • This should be the correct answer as it doesn't re-invent the wheel while not having to read the entire file at once before reading it. – Eli Davis Mar 19 '20 at 13:50
  • @ShayTsadok Isn't closing the file will sync the data? – x-yuri Feb 04 '23 at 03:34
  • @x-yuri No, it won't, see the man page quotation at https://stackoverflow.com/questions/705454/does-linux-guarantee-the-contents-of-a-file-is-flushed-to-disc-after-close. This is the notorious Linux gotcha: put file to USB drive, pull out the USB drive, and only lean afterwards that the file was not written... That's why on Linux, if you write anything to USB drive, you _have to_ explicitly unmount. – user7610 Feb 04 '23 at 15:48
24

Note. ioutil is deprecated since Go 1.16.

With newer Go versions, reading/writing to/from a file is easy. To read from a file:

package main

import (
    "fmt"
    "io/ioutil"
)

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

To write to a file:

package main

import "os"

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

    file.WriteString("test\nhello")
}

This will overwrite the content of a file (create a new file if it was not there).

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Salvador Dali
  • 214,103
  • 147
  • 703
  • 753
10

[]byte is a slice (similar to a substring) of all or part of a byte array. Think of the slice as a value structure with a hidden pointer field for the system to locate and access all or part of an array (the slice), plus fields for the length and capacity of the slice, which you can access using the len() and cap() functions.

Here's a working starter kit for you, which reads and prints a binary file; you will need to change the inName literal value to refer to a small file on your system.

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();
}
peterSO
  • 158,998
  • 31
  • 281
  • 276
7

Note. Since Go 1.16 ReadFile is in the os package.

Try this:

package main

import (
  "io"; 
  )
  

func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0644);
}
x-yuri
  • 16,722
  • 15
  • 114
  • 161
marketer
  • 41,507
  • 11
  • 37
  • 40
4

you can use the fmt package also:

package main

import "fmt"

func main(){
    file, err := os.Create("demo.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    
    fmt.Fprint(file, name)
}
1

Just looking at the documentation it seems you should just declare a buffer of type []byte and pass it to read which will then read up to that many characters and return the number of characters actually read (and an error).

The docs say

Read reads up to len(b) bytes from the File. It returns the number of bytes read and an Error, if any. EOF is signaled by a zero count with err set to EOF.

Does that not work?

EDIT: Also, I think you should perhaps use the Reader/Writer interfaces declared in the bufio package instead of using os package.

Hannes Ovrén
  • 21,229
  • 9
  • 65
  • 75
  • You have my vote because you actually acknowledge what real people see when they read the documentation, instead of parrotting what those accustomed to Go are REMINDED OF (not reading REMINDED OF) when they read the documentation of the function they are familiar with already. – cardiff space man Jul 22 '12 at 04:00
1

The Read method takes a byte parameter because that is the buffer it will read into. It's a common idiom in some circles and makes some sense when you think about it.

This way you can determine how many bytes will be read by the reader and inspect the return to see how many bytes actually were read and handle any errors appropriately.

As others have pointed in their answers, bufio is probably what you want for reading from most files.

I'll add one other hint since it's really useful. Reading a line from a file is best accomplished not by the ReadLine method, but the ReadBytes or ReadString method instead.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jeremy Wall
  • 23,907
  • 5
  • 55
  • 73