循环中的bufio ReadString是无限的

时间:2017-11-24 20:22:00

标签: go

我有下一个代码:

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

if err != nil{
    panic(err)
}

r := bufio.NewReader(resp.Body)

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

所以,我想在循环中读取回复体,但是读者r无限地读取Body的第一行。

虽然在同一时间,这段代码工作正常:

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

有人可以解释为什么第一种解决方案会出现这种行为吗?

2 个答案:

答案 0 :(得分:2)

  

Package bufio

import "bufio"
     

func (*Reader) ReadString

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

ReadString读取直到输入中第一次出现delim,   返回包含数据的字符串,包括   分隔符。如果ReadString在找到之前遇到错误   分隔符,它返回错误和错误之前读取的数据   本身(通常是io.EOF)。 ReadString返回err!= nil当且仅当   返回的数据不以delim结尾。对于简单的用途,扫描仪   可能更方便。

这是XY problem:XY问题是询问您尝试的解决方案而不是实际问题。

为什么没有接受建议,"对于简单的用途,扫描仪可能更方便",在bufio.ReadString文档中给出了?

即使您知道如何使用bufio.ReadString循环,正确使用for也很复杂。请参阅函数reader

即使您不知道如何使用bufio.Scannner循环,正确使用for也很简单。请参阅函数scanner

例如,

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('\n')
        if len(line) == 0 && err != nil {
            if err == io.EOF {
                break
            }
            return err
        }
        line = strings.TrimSuffix(line, "\n")

        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("\nReader:\n")
    err := reader(url)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
    fmt.Println("\nScanner:\n")
    err = scanner(url)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
    fmt.Println("\n")
}

游乐场:https://play.golang.org/p/e0WY_aNxW8

答案 1 :(得分:0)

循环的结构是:

for init; condition; post { }

循环的init部分在开头只调用一次。这意味着......

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

...来自循环的部分仅被调用一次,这解释了为什么您的循环实现只读取r的第一行以及为什么e始终为nil,从而导致无限循环。

你可能想做这样的事情:

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

或者如果这对你来说很奇怪,就像这样:

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

更多信息: