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

Setting Atom for Golang on Windows

Golang is another programming language I recently become addicted to. I am a C programmer, and Golang is very similar to it but offers more high-level language’s functionalities e.g. built-in complex structs (slice, map), OOP (kind of) and concurrency (goroutine, channel). This shortens development time like one using Python yet has better performance.

I just bought a laptop, and it is pre-installed with Windows. I’m waiting for Ubuntu 16.04 to come out. That means I have to be nice with Windows for the time being. I enrolled a Golang course on Pluralsight. It teaches how to setup an environment using Atom with Gulp.js on Windows. Gulp.js is used to look for changes made under a working project’s directory. Any change made triggers gocode (autocompletion server) to update its database. It works nicely in their videos, but too bad that I couldn’t do it. So, I looked up the internet and found a way to ditch Gulp.js while functioning the same. And here, this article tells how I get along with Go on Atom on Windows.

Setup Atom Editor

  1. Get Atom editor – It’s free!
  2. Get plugins
    • go-plus – adds extra Atom functionality for the go language
    • atom-terminal – open a command prompt in the current file’s directory (`alt-shift-t`)
  3. Create a project root directory e.g. `C:\Users\pipat\Desktop\hello-go`
  4. Configure go-plus’s `%GOPATH%` environment variable to the project root directory
  5. Get Golang tools
    • From the menu bar `Packages > Go Plus > Get Missing Tools`
      • This downloads source code and install it under `%GOPATH%`

This slideshow requires JavaScript.

  1. Configure gocode to watch for changes
    1. Open a cmd (`alt-shift-t`)
    2. Navigate to your `%GOPATH%` directory
    3. Enter `bin\gocode.exe set autobuild true`

gocode-set-autobuild

After these steps, your Atom has Golang autocompletion and linting features. Be sure to change GOPATH in go-plus every time you switch to another working directory.

Continue reading