Read and Write a File in Golang

Open a file

The os package allows a Go program to interface with files. The code below shows the pattern.

package main

import (
  "os"
  "log"
)

func main() {
  file, err := os.Open("file1.txt")  // O_RDONLY mode
  if err != nil {
    log.Fatal(err)
  }
  defer file.Close()
}

Open() opens a file with read only flag. There are several modes declared in the package.

const (
  O_RDONLY int = syscall.O_RDONLY // open the file read-only.
  O_WRONLY int = syscall.O_WRONLY // open the file write-only.
  O_RDWR   int = syscall.O_RDWR   // open the file read-write.
  O_APPEND int = syscall.O_APPEND // append data to the file when writing.
  O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
  O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist
  O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
  O_TRUNC  int = syscall.O_TRUNC  // if possible, truncate file when opened.
)

To open a file with other flags, OpenFile() has to be used. OpenFile() also needs another paramter, permission. A permission value is identical to *NIX systems i.e. rwxrwxrwx. Open() uses 0666 permission value.

// Open as read-only
file, err := os.Open("file.txt")
// Same as above
file, err := os.OpenFile("file.txt", os.O_RDONLY, 0666)
// Open as write-only
file, err := os.OpenFile("file.txt"), os.O_WRONLY, 0666)

fmt.Fscanf()

This is probably the easiest way to deal with text files. Chances are you are writing programs for a programming contest, and you have finished the logic but don’t know how to feed them inputs from files.

The fmt package comes with functions to deal with formatted I/O (stdio, string and file) analogous to C’s printf() and scanf(). The table below shows its function variants.

stdio string file
READ
scan Scan Sscan Fscan
scanf Scanf Sscanf Fscanf
scanln Scanln Sscanln Fscanln
WRITE
print Print Sprint Fprint
printf Printf Sprintf Fprintf
println Println Sprintln Fprintln
  • scan variants scan its input and assign it to its variables (arguments) respectively.
  • scanf variants are similar to ones in C. They scan for values according to its format string. The difference between Golang and C is that Golang treats a newline as a newline while C treats a newline as a space.
  • scanln variants are similar to scan variants except they scan until finding a newline.

  • print variants print each argument by its default format (%v) with a space separating each value.

  • printf variants are identical to ones in C. A format string is defined as the first argument. Following arguments fill out its value in the format string in order.
  • println variants are similar to the print variants except they put a newline at the end.

Here is an example.

// XXX: "input.txt" and "output.txt" must already exist
// XXX: Skip error handling to save space
inputFile, err := os.Open("input.txt")
defer inputFile.Close()
outputFile, err := os.OpenFile("output.txt", os.O_WRONLY, 0666)
defer outputFile.Close()
var a, b int
var itemCount int
itemCount, err = fmt.Fscanf(inputFile, "%d %d\n", &a, &b)
for itemCount > 0 && err == nil {
  fmt.Fprintln(outputFile, b, a)
  itemCount, err = fmt.Fscanln(inputFile, &a, &b)
}

This program swaps numbers in input.txt and writes to output.txt. In C, opening a file in write mode creates a new blank file. In Go, it is much more manual. O_WRONLY flag says “just point to the beginning of the file and start (over)writing”. The following statement has the same behavior as C.

outputFile, err := os.OpenFile("output.txt", 
  os.O_WRONLY | os.O_CREATE | os.O_TRUNC,
  0666)

File.Read()

This is a bare-feet approach to get to a file’s content. Every action is done in bytes via a buffer. For read, you open a file and store everything read to a buffer. For write, you open a file and put your buffer to a file.

// XXX: Skip error handling and file closing to save space
var buffer []bytes
buffer = make([]byte, 100)
inputFile, err := os.Open("input.txt")
n, err := file.Read(buffer)
fmt.Printf("(Bytes read: %v) %v\n", n, buffer)
outputFile, err := os.OpenFile("output.txt", os.O_WRONLY, 0666)
n, err = file.Write(buffer)
fmt.Printf("(Bytes written: %v) %v\n", n, buffer)

ReadAt() and WriteAt() are similar to its siblings but they can start with an offset.

Seek() moves the location pointed by a File variable relative to one of these positions.

SEEK_SET int = 0 // seek relative to the origin of the file
SEEK_CUR int = 1 // seek relative to the current offset
SEEK_END int = 2 // seek relative to the end

ioutil.ReadFile()

Instead of reading little by little in os.Read(), ioutil.ReadFile() reads a whole file into a buffer. err is nil if it successes.

ioutil.WriteFile() also does thing in bulk as ReadFile(). The function behaves like C’s fopen(filename, "w"). It creates a new file if it does not exist and truncates if it exists.

Another thing to notice about this family is that it uses a path string to access a file instead of a File variable.

import "io/ioutil"
// XXX: Skip error handling to save space
inputBytes, err := ioutil.ReadFile("input.txt")
err = ioutil.WriteFile("output.txt", inputBytes, 0666)

In the package, there are other functions to ioutil.TempDir() and ioutil.TempFile().

Scanner.Scan()

bufio package eases buffered operations. That means you just have to open a file for its functions. To read, you have to create a Scanner variable, which reads from a file and store in its private buffer.

// XXX: Skip error handling to save space
inputFile, err := os.Open("input.txt")
inputScanner := bufio.NewScanner(inputFile)
inputScanner.Split(bufio.ScanLines)  // Read until a newline for each Scan() (Default)
for inputScanner.Scan() {
  fmt.Println(inputScanner.Text())   // get the buffered content as string
  fmt.Println(inputScanner.Bytes())  // same content as above but as []byte
}

Writer is a bit decorated []byte buffer. You have to Flush() a buffer to a file mannually. It can be used in combination with fmt.Fprintf().

// XXX: Skip error handling to save space
outputFile, err := os.OpenFile("output.txt", os.O_WRONLY, 0666)
defer outputFile.Close()
outputWriter := bufio.NewWriter(outputFile)
// fmt.Printf("There are %v bytes available", outputWriter.Available())
fmt.Fprintf(outputWriter, "Hello World\n")  // formatted write to the buffer
// fmt.Printf("There are %v bytes available", outputWriter.Available())
outputWriter.WriteString("============\n")  // another way to write to the buffer
// fmt.Printf("There are %v bytes used", outputWriter.Buffered())
outputWriter.Flush()  // Finally write to the file

References

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s