在循环中进入bufio ReadString是无限的

I have the next code:

resp, err := http.Get("https://www.google.com")

if err != nil{
    panic(err)
}

r := bufio.NewReader(resp.Body)

for v, e := r.ReadString('
'); e == nil; {
    fmt.Println(v)
}

So, I want to read responce body in loop but reader r reads first line of Body infinitely.

While in the same time, this code works fine:

v, e := r.ReadString('
')
for e == nil {
    fmt.Println(v)
    v, e = r.ReadString('
')
}

Can someone explain why the first solution has such behaviour?

Structure of the loop is:

for init; condition; post { }

The init part of the loop is called only once, at the beginning. That means that the...

v, e := r.ReadString('
')

...part from your loop is called only once, which explains why your loop implementation reads only the first line from r and why e is always nil, resulting in an infinite loop.


You may want to do something like this instead:

for v, e := "", (error)(nil); e == nil; {
    v, e = r.ReadString('
')
    fmt.Println(v)
}

Or if that looks weird to you, something like this:

var v string
var e error
for ; e == nil; {
    v, e = r.ReadString('
')
    fmt.Println(v)
}

More info here:

Package bufio

import "bufio"

func (*Reader) ReadString

func (b *Reader) ReadString(delim byte) (string, error)

ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter. If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF). ReadString returns err != nil if and only if the returned data does not end in delim. For simple uses, a Scanner may be more convenient.


This is an XY problem: The XY problem is asking about your attempted solution rather than your actual problem.

Why didn't you take the advice, "For simple uses, a Scanner may be more convenient", given in the bufio.ReadString documentation?

Proper use of bufio.ReadString is complicated, even when you know how to use for loops. See function reader.

Proper use of bufio.Scannner is simple, even if you don't know how to use for loops. See function scanner.

For example,

package main

import (
    "bufio"
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"
)

func reader(url string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    // ReadString
    r := bufio.NewReader(resp.Body)
    for {
        line, err := r.ReadString('
')
        if len(line) == 0 && err != nil {
            if err == io.EOF {
                break
            }
            return err
        }
        line = strings.TrimSuffix(line, "
")

        fmt.Println(line)

        if err != nil {
            if err == io.EOF {
                break
            }
            return err
        }
    }

    return nil
}

func scanner(url string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    // Scanner
    s := bufio.NewScanner(resp.Body)
    for s.Scan() {
        line := s.Text()

        fmt.Println(line)

    }
    if s.Err() != nil {
        return err
    }

    return nil
}

func main() {
    url := "https://www.example.com"
    fmt.Println("
Reader:
")
    err := reader(url)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
    fmt.Println("
Scanner:
")
    err = scanner(url)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
    fmt.Println("
")
}

Playground: https://play.golang.org/p/e0WY_aNxW8