Golang Reader Example

Go is famous for having very small interfaces in its standard library. One of them is the io.Reader interface. The io.Reader interface is used by many packages in the Go standard library and it represents the ability to read a stream of data. More specifically allows you to read data from something that implements the io.Reader interface into a slice of bytes. Here’s the io.Reader interface definition


Read(b []byte) (n int, err error)

This method populates the data into the byte slice b and returns the number of bytes read n and an error err if there was any. It will return an error of type io.EOF if there were no more bytes to read from the stream.

The io.Read interface is being implemented by many packages in the Go standard library to deal with a lot of different scenarios, but all related to reading bytes from a source, such as, reading bytes from files, network connections and so on

io.Reader to read from a file

Here’s an example on how io.Reader is used when reading a file in Go

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
    f, err := os.Open("/tmp/123")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    // read 1024 bytes at a time
    buf := make([]byte, 1024)

    for {
        n, err := f.Read(buf)
        if err == io.EOF {
            // there is no more data to read
            break
        }
        if err != nil {
            fmt.Println(err)
            continue
        }
	if n > 0 {
            fmt.Print(buf[:n])
        }
    }
}

os.File’s Read is indeed an implementation of the same io.Reader interface which is used to read a stream of bytes from a os file. Here’s the definition of os.File.Read

func (f *File) Read(b []byte) (n int, err error)

io.Reader to read from a string

As io.Reader is an abstraction it can be used to read a stream of data from different sources. For example from a string using strings.NewReader

package main

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

func main() {
    r := strings.NewReader("hello world.")
    buf := make([]byte, 1)
    for {
        n, err := r.Read(buf)
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Println(err)
            continue
        }
	if n > 0 {
            fmt.Println(string(buf[:n]))
        }
    }
}

This will read a string content one byte at a time followed by a new line. The expected result should be the following

h
e
l
l
o
 
w
o
r
l
d
.

io.Reader to read from a Network Connection

The io.Reader interface is being implemented also by the net package in the Conn type, so that you can read bytes from a network connection using the same interface we have just used so far

package main

import (
	"fmt"
	"net"
	"op"
)

func main() {
    conn, err := net.Dial("tcp", "google.com:80")
    if err != nil {
        panic(err)
    }
    defer conn.Close()
    fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
    buf := make([]byte, 512)
    for {
        n, err := conn.Read(buf)
        if err == io.EOF {
            break
        }
        if err != nil {
                fmt.Println(err)
                continue
            }
	if n > 0 {
            fmt.Println(string(buf[:n]))
        }
    }
}

This example reads data from a network connection, in this case a simple HTTP request to google.com at port 80 using the TCP protocol. The expected result should just be a HTTP Response

HTTP/1.0 301 Moved Permanently

Location: http://www.google.com/

Content-Type: text/html; charset=UTF-8

Server: gws

Content-Length: 219

X-XSS-Protection: 0

X-Frame-Options: SAMEORIGIN

Age: 19

Date: Sat, 03 Oct 2020 16:28:55 GMT

Expires: Mon, 02 Nov 2020 16:28:55 GMT

Cache-Control: public, max-age=2592000

Connection: close

  

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">

<TITLE>301 Moved</TITLE></HEAD><BODY>

<H1>301 Moved</H1>

The document has moved

<A HREF="http://

www.google.com/">here</A>.

</BODY></HTML>


Got Questions? Join our Slack community