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:
import "bufio"
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