Nav
Menu
Content
  1. Introduction
  2. Writer Interface
  3. Reader Interface
    1. Examples:
      1. ReadFull func ReadFull(r Reader, buf []byte) (n int, err error)
      2. ReadAtLeast
      3. LimitReader
      4. MultiReader
  4. ReaderAt Interface

Golang Notes: Basic IO Interface

Introduction

The io package provides the basic interface for I/O primitives in go. In the io package there are two important interfaces:

  1. Writer
  2. Reader

Writer Interface

Official Documentation

The Writer interface is defined as:

1
2
3
type Writer interface {
Write(p []byte) (n int, err error)
}

Examples of Impelmenting io.Writer

  1. A Write Counter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Counter is an alias type of int
type Counter int

func main() {
fmt.Println("Write Counter")

var c Counter

c.Write([]byte{0})
c.Write([]byte{0})
c.Write([]byte{0})
c.Write([]byte{0})
c.Write([]byte{0})
c.Write([]byte{0})

fmt.Println("Writer Counter: ", c)
}

// Implementing Writer interface as Counter type
func (c *Counter) Write(p []byte) (n int, err error) {
*c = Counter(int(*c) + 1)
return len(p), nil
}
  1. A byte counter Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Counter is an alias type of int
type Counter int

func main() {
fmt.Println("Bytes Counter")

var c Counter

c.Write([]byte{0, 1})
c.Write([]byte("Hello World"))
c.Write([]byte{0})
c.Write([]byte{0})
c.Write([]byte{0})
c.Write([]byte{0})

fmt.Println("No Of Bytes Written: ", c)
}

// Implementing Writer interface as Counter type
func (c *Counter) Write(p []byte) (n int, err error) {
*c = Counter(int(*c) + len(p))
return len(p), nil
}
  1. Implement io.Writer for ‘person Struct’
    • Turn a slice of bytes, []byte, into valid person Object

Reader Interface

The Reader interface is defined as:

1
2
3
type Reader interface {
Read(p []byte) (n int, err error)
}

A Simple Example of Byte Counter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
)

type ReadCounter struct {
count uint64
}

func main () {
fmt.Println("Read Counter")
var rc ReadCounter
var s0 []byte
var s1 = make([]byte, 20)
var s2 = make([]byte, 156)
rc.Read(s0)
rc.Read(s1)
rc.Read(s2)
fmt.Println(rc)
}

// Implementing the Reader Interface
func (r *ReadCounter) Read(p []byte)(n int, err error) {
r.count++
return len(p), nil
}
func (r ReadCounter) String() string {
return fmt.Sprintf("%v", r.count)
}
  1. Encoding an object of ‘Person’ to a series of bytes

Official Documentation Summary:

The Read method read len(p) bytes into p, returning the number of bytes read and the error encountered. When Read methods returns an error, it does not mean that no data is being read. The caller should process any data returned before dealing with the possible errors.

Examples:

ReadFull func ReadFull(r Reader, buf []byte) (n int, err error)

So ReadFull Can read any object’s data that meet the Reader interface criteria.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "io"
import "fmt"

// Ustr Types
type Ustr struct {
s string // data
i int // location of read and write
}

// NewUstr Create Ustr object
func NewUstr(s string) *Ustr {
return &Ustr{s, 0}
}

// Len returns the data length of the unread portion
func (s *Ustr) Len() int {
return len(s.s) - s.i
}

// Read method of the Ustr type
func (s *Ustr) Read(p []byte) (n int, err error) {
// Converts lowercase letters to uppercase letters and then write to p
for ; s.i < len(s.s) && n < len(p); s.i++ {
c := s.s[s.i]
if 'a' <= c && c <= 'z' {
p[n] = c + 'A' - 'a'
} else {
p[n] = c
}
n++
}
if n == 0 {
return n, io.EOF
}
return n, nil
}

func main() {
s := NewUstr("Hello World!")
buf := make([]byte, s.Len())
// ReadFull method to read the data of the Ustr object
n, err := io.ReadFull(s, buf) // Read Data s into buf
fmt.Printf("%s\n", string(buf))
fmt.Println(buf)
fmt.Println(n, err)
}

Output:

1
2
3
HELLO WORLD!
[72 69 76 76 79 32 87 79 82 76 68 33]
12 <nil>

ReadAtLeast

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import (
"fmt"
"io"
"strings"
)

// ExampleRdLeast type
type ExampleRdLeast struct {
BufferLength int
MinimumRead int
Message string
}

// ShowExampleRdLeast demo : the ReadAtLeast example
func ShowExampleRdLeast(ex ExampleRdLeast) {
rd := strings.NewReader(ex.Message) // a Reader type
buff := make([]byte, ex.BufferLength)
// Read at Least min bytes into buffer, returning the
// standard bytes read and any error
bytesRead, err := io.ReadAtLeast(rd, buff, ex.MinimumRead)
fmt.Printf("len(buffer)=%d, min=%d, bytesRead=%d, err=%v, (%s)\n",
ex.BufferLength, ex.MinimumRead, bytesRead, err, ex.Message)
}

func main() {
ex := []ExampleRdLeast{
{10, 5, "OK; read less than buf can handle, plenty of data"},
{100, 75, "Unexpected EOF; buf has space, but ran out of data"},
{10, 15, "Short buffer; trying to read more than buf can handle"},
}
for _, exa := range ex {
ShowExampleRdLeast(exa)
}
}

Output:

1
2
3
len(buffer)=10, min=5, bytesRead=10, err=<nil>, (OK; read less than buf can handle, plenty of data)
len(buffer)=100, min=75, bytesRead=50, err=unexpected EOF, (Unexpected EOF; buf has space, but ran out of data)
len(buffer)=10, min=15, bytesRead=0, err=short buffer, (Short buffer; trying to read more than buf can handle)

LimitReader

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"io"
"os"
"strings"
)

func main() {
r := strings.NewReader("Hello World!")
lr := io.LimitReader(r, 5)
_, _ = io.Copy(os.Stdout, lr)
}

MultiReader

A MultiReader , read from multiple readers, one after the other. It reads everything from eveything in order.

ReaderAt Interface

1
2
3
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}

ReaderAt interface makes it possible to read data from the specified offset

1
2
3
4
5
6
7
func main() {
reader := strings.NewReader("Hello World")
p := make([]byte, 6)
n, err := reader.ReadAt(p, 5)
fmt.Println(string(p), n, err)

}