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 Conntype, 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>