io.Reader interface

io.Reader interface is a basic and very frequently-used interface:

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

For every type who satisfies the io.Reader interface, you can imagine it’s a pipe. Someone writes contents into one end of the pipe, and you can use Read() method which the type has provided to read content from the other end of the pipe. No matter it is a common file, a network socket, and so on. Only if it is compatible with io.Reader interface, I can read content of it.

Let’s see an example:

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "os"
  7. )
  8. func main() {
  9. file, err := os.Open("test.txt")
  10. if err != nil {
  11. log.Fatal(err)
  12. }
  13. defer file.Close()
  14. p := make([]byte, 4)
  15. for {
  16. if n, err := file.Read(p); n > 0 {
  17. fmt.Printf("Read %s\n", p[:n])
  18. } else if err != nil {
  19. if err == io.EOF {
  20. fmt.Println("Read all of the content.")
  21. break
  22. } else {
  23. log.Fatal(err)
  24. }
  25. } else /* n == 0 && err == nil */ {
  26. /* do nothing */
  27. }
  28. }
  29. }

You can see after issuing a read() call, there are 3 scenarios need to be considered:

(1) n > 0: read valid contents; process it;
(2) n == 0 && err != nil: if err is io.EOF, it means all the content have been read, and there is nothing left; else something unexpected happened, need to do special operations;
(3) n == 0 && err == nil: according to io package document, it means nothing happened, so no need to do anything.

Create a test.txt file which only contains 5 bytes:

  1. # cat test.txt
  2. abcde

Executing the program, and the result is like this:

  1. Read abcd
  2. Read e
  3. Read all of the content.

Reference:
io package document.