goioreaderstringreader

golang reader.Read() can arrived EOF, but reader.ReadAt() read same data in dead loop way, not arrived EOF?


I'm trying to learn the diff in io.Reader.Read() and io.Reader.ReadAt(); And I write two example, the first is io.Reader.Read():

    reader := strings.NewReader("some text heresome text heres, ome text heresome text heresome text here")

    buf := make([]byte, 64)

    i := 0
    for {
        i++
        if i > 10 {
            break
        }

        n, err := reader.Read(buf)

        if n > 0 {
            fmt.Println(string(buf[:n]))
        }

        fmt.Println(err)

        if err == io.EOF {
            fmt.Println("All content is over...")
            break
        } else if err != nil {
            panic(err.Error())
        }
    }

and the output is:

some text heresome text heres, ome text heresome text heresome t
<nil>
ext here
<nil>
EOF
All content is over...

the second example just replace the Read() to ReadAt():

    reader := strings.NewReader("some text heresome text heres, ome text heresome text heresome text here")

    buf := make([]byte, 64)

    i := 0
    for {
        i++
        if i > 10 {
            break
        }

        n, err := reader.ReadAt(buf, 2)

        if n > 0 {
            fmt.Println(string(buf[:n]))
        }

        fmt.Println(err)

        if err == io.EOF {
            fmt.Println("All content is over...")
            break
        } else if err != nil {
            panic(err.Error())
        }
    }

And the result unexpected not arrived the EOF, and deep loop occured if I cancel times limit: output:

me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>
me text heresome text heres, ome text heresome text heresome tex
<nil>

I expected the second example code output similar with first code example, just offset 2 bytes. but why the code into dead loop?


Solution

  • Reading from an io.Reader using its Read() method, the reader's internal pointer (offset) advances by the number of read (returned) bytes. The next Read() call will read bytes from the new, adjusted offset.

    When you call ReadAt() method, you explicitly provide the offset to read at, the internal pointer / offset of the io.Reader is not used. And since the buffer you read into is smaller than the available data of the source reader (starting at the given offset), you won't get an io.EOF, and you'll just read the same data from the same offset over and over again.

    Note that if you increase the buffer:

    buf := make([]byte, 164)
    

    This way all the data available at the source can be read into it, and you'll get an EOF immediately (try it on the Go Playground):

    me text heresome text heres, ome text heresome text heresome text here
    EOF
    All content is over...