17. File Handling

For the programs that follow, please download, install and setup Go as per Appendix A. Do not use the Go Playground.

17.1 Package ioutil

Usage: import "io/ioutil"

Documentation: ioutil1

The function ioutil.ReadFile(filename string) reads the entire contents of the file filename into memory.

Type and store the following program in the same folder where you had written hello.go:

Program: read_file_ioutil.go


 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"io/ioutil"
 6 	"log"
 7 )
 8 
 9 func check(e error) {
10 	if e != nil {
11 		log.Fatal(e)
12 	}
13 }
14 
15 func main() {
16 	dat, err := ioutil.ReadFile("hello.go")
17 	check(err)
18 	fmt.Print(string(dat))
19 }

The program will output the contents of the file hello.go.

func ReadFile(filename string) ([]byte, error)

ReadFile reads the file named by filename and returns the contents. A successful call returns err == nil, not err == EOF. Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported.

17.2 Package os

Usage: import "os"

Documentation: http://golang.org/pkg/os/

Package os provides a platform-independent interface to operating system functionality.

Let us look at some programs in this section.

17.2.1 read_file_os1.go

First write the program read_file_os1.go in the same folder where you had written hello.go as follows:

Program: read_file_os1.go


 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"log"
 6 	"os"
 7 )
 8 
 9 func check(e error) {
10 	if e != nil {
11 		log.Fatal(e)
12 	}
13 }
14 
15 func main() {
16 	file, err := os.Open("hello.go") // For read access
17 	check(err)
18 	defer file.Close()
19 
20 	// get the file size
21 	stat, err := file.Stat()
22 	check(err)
23 
24 	// read the file
25 	bs := make([]byte, stat.Size())
26 	_, err = file.Read(bs)
27 	check(err)
28 
29 	str := string(bs) // type string
30 	fmt.Println(str)
31 }

The above program is an example of how to read the contents of a file and display them on the terminal.

17.2.1.1 Stat
1 func (f *File) Stat() (fi FileInfo, err error)

Stat returns the FileInfo structure describing file. If there is an error, it will be of type *PathError.

17.2.1.2 FileInfo

A FileInfo describes a file and is returned by Stat and Lstat.

Here’s the FileInfo interface:

1 type FileInfo interface {
2         Name() string       // base name of the file
3         Size() int64        // length in bytes for regular files;
4                             // system-dependent for others
5         Mode() FileMode     // file mode bits
6         ModTime() time.Time // modification time
7         IsDir() bool        // abbreviation for Mode().IsDir()
8         Sys() interface{}   // underlying data source (can return nil)
9 }

Observe that Size() gives us the length in bytes for regular files.

17.2.2 read_file_os2.go

Next write the second program read_file_os2.go in the same folder where you had written hello.go as follows:

Program: read_file_os2.go


 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"log"
 6 	"os"
 7 )
 8 
 9 func check(e error) {
10 	if e != nil {
11 		log.Fatal(e)
12 	}
13 }
14 
15 func main() {
16 	file, err := os.Open("hello.go") // For read access
17 	check(err)
18 	defer file.Close()
19 
20 	//The file's data can then be read into a slice
21 	//of bytes. Read and Write take their byte counts
22 	//from the length of the argument slice.
23 	//Read some bytes from the beginning of
24 	//the file. Allow up to 5 to be read but
25 	//also note how many actually were read.
26 	slice1 := make([]byte, 5)
27 	n1, err := file.Read(slice1)
28 	check(err)
29 	fmt.Printf("%d bytes: %s\n", n1, string(slice1))
30 
31 	//You can also Seek to a known location in
32 	//the file and Read from there.
33 	o2, err := file.Seek(6, 0)
34 	check(err)
35 	slice2 := make([]byte, 2)
36 	n2, err := file.Read(slice2)
37 	check(err)
38 	fmt.Printf("%d bytes @ %d: %s\n", n2, o2, string(slice2))
39 }

Part of the output is shown below:

5 bytes: // Si
2 bytes @ 6: gl
17.2.2.1 seek function:
1 func (f *File) Seek(offset int64, whence int) (ret int64, err error)

Seek sets the offset for the next Read or Write on file to offset, interpreted according to whence: 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end. It returns the new offset and an error, if any.

Note: There is no built-in rewind, but Seek(0, 0) accomplishes this.

17.2.3 write_file_os.go

The next program write_file_os.go is an example on how we can create a file:

Program: write_file_os.go


 1 package main
 2 
 3 import (
 4 	"log"
 5 	"os"
 6 )
 7 
 8 func check(e error) {
 9 	if e != nil {
10 		log.Fatal(e)
11 	}
12 }
13 
14 func main() {
15 	file, err := os.Create("test.txt")
16 	check(err)
17 	defer file.Close()
18 
19 	file.WriteString("test")
20 
21 	//Issue a Sync to flush writes
22 	//to stable storage.
23 	file.Sync()
24 }

17.2.3.1 Create
1 func Create(name string) (file *File, err error)

Create creates the named file mode 0666 (before umask), truncating it if it already exists. If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR. If there is an error, it will be of type *PathError.

17.2.3.2 WriteString
1 func (f *File) WriteString(s string) (ret int, err error)

WriteString writes the contents of string s.

17.2.4 read_dir_os.go

The next program read_dir_os.go is an example on how to get the contents of a directory:

Program: read_dir_os.go


 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"log"
 6 	"os"
 7 )
 8 
 9 func check(e error) {
10 	if e != nil {
11 		log.Fatal(e)
12 	}
13 }
14 
15 func main() {
16 	dir, err := os.Open(".")
17 	check(err)
18 	defer dir.Close()
19 
20 	fileInfos, err := dir.Readdir(-1)
21 	check(err)
22 	for _, fi := range fileInfos {
23 		fmt.Println(fi.Name())
24 	}
25 }

17.2.4.1 Readdir
1 func (f *File) Readdir(n int) (fi []FileInfo, err error)

Readdir reads the contents of the directory associated with file and returns a slice of up to n ``FileInfo values, in directory order. Subsequent calls on the same file will yield further FileInfos.

If n > 0, Readdir returns at most n FileInfo structures. In this case, if Readdir returns an empty slice, it will return a non-nil error explaining why. At the end of a directory, the error is io.EOF.

If n <= 0, Readdir returns all the FileInfo from the directory in a single slice. In this case, if Readdir succeeds (reads all the way to the end of the directory), it returns the slice and a nil error. If it encounters an error before the end of the directory, Readdir returns the FileInfo read until that point and a non-nil error.

17.2.4.2 Name()

Name() returns a string which is the base name of the file.

17.3 Package bufio

Usage: import "bufio"

Documentation: bufio

Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O.

Let us write a program read_bufio.go as follows:

Program: read_bufio.go


 1 package main
 2 
 3 import (
 4 	"bufio"
 5 	"fmt"
 6 	"log"
 7 	"os"
 8 )
 9 
10 func check(e error) {
11 	if e != nil {
12 		log.Fatal(e)
13 	}
14 }
15 
16 func main() {
17 	file, err := os.Open("hello.go")
18 	check(err)
19 	defer file.Close()
20 
21 	reader := bufio.NewReader(file)
22 
23 	b4, err := reader.Peek(5)
24 	check(err)
25 	fmt.Printf("5 bytes: %s\n", string(b4))
26 }

17.3.1 NewReader

1 func NewReader(rd io.Reader) *Reader

NewReader returns a new Reader whose buffer has the default size.

17.3.2 Peek

1 func (b *Reader) Peek(n int) ([]byte, error)

Peek returns the next n bytes without advancing the reader. The bytes stop being valid at the next read call. If Peek returns fewer than n bytes, it also returns an error explaining why the read is short. The error is ErrBufferFull if n is larger than b’s buffer size.

Now let us write a program write_bufio.go as follows:

Program: write_bufio.go


 1 package main
 2 
 3 import (
 4 	"bufio"
 5 	"fmt"
 6 	"log"
 7 	"os"
 8 )
 9 
10 func check(e error) {
11 	if e != nil {
12 		log.Fatal(e)
13 	}
14 }
15 
16 func main() {
17 	f, err := os.Create("test2.txt")
18 	check(err)
19 	defer f.Close()
20 
21 	w := bufio.NewWriter(f)
22 	n4, err := w.WriteString("buffered\n")
23 	fmt.Printf("wrote %d bytes\n", n4)
24 
25 	w.Flush()
26 }

The output is:

wrote 9 bytes

17.3.3 NewWriter

1 func NewWriter(wr io.Writer) *Writer

NewWriter returns a new Writer whose buffer has the default size.

Exercise 16

Manipulate a text file. A plain text file has the contents as shown below in “Original File”. Observe that in this file, there exists a word ‘word’. Write a Go program that updates this file and the final contents become as shown in “Modified Content” below.

Original file contents:

text text text text text
text text text text text
text text word text text
text text text text text
text text text text text

Modified file contents:

text text text text text
text text text text text
text text inserted word text text
text text text text text
text text text text text

Exercise 17

Analyze an MP3 File. Write a Go program that analyzes an MP3 file. Many MP3 files have a 128-byte data structure at the end called an ID3 tag. These 128 bytes are literally packed with information about the song: its name, the artist, which album it’s from, and so on. You can parse this data structure by opening an MP3 file and doing a series of reads from a position near the end of the file. According to the ID3 standard, if you start from the 128th-to-last byte of an MP3 file and read three bytes, you should get the string TAG. If you don’t, there’s no ID3 tag for this MP3 file, and nothing to do. If there is an ID3 tag present, then the 30 bytes after TAG contain the name of the song, the 30 bytes after that contain the name of the artist, and so on. A sample song2 is available to test your program.

17.4 Solutions

Exercise 16 Solution:

Exercise 17 solution:

Fun2

Fun2

WAIT… If you have liked the course so far, I am confident that you will like the latter part of the course too. This is where the fun begins.

Fun4 Glad to have you back…

  1. http://golang.org/pkg/io/ioutil/
  2. http://rubylearning.com/data/song.mp3