意外的EOF与fmt.Scanner

huangapple go评论109阅读模式
英文:

unexpected EOF with fmt.Scanner

问题

如果我想遍历一个字符串,可以这样做:

  1. package main
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. func main() {
  7. r := strings.NewReader("west north east")
  8. for {
  9. var s string
  10. _, e := fmt.Fscan(r, &s)
  11. fmt.Printf("%q %v\n", s, e)
  12. if e != nil { break }
  13. }
  14. }

结果:

  1. "west" <nil>
  2. "north" <nil>
  3. "east" <nil>
  4. "" EOF

我最近发现了fmt.Scanner[1],所以我想尝试实现它。我想出了这个:

  1. package main
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. type comma struct { tok string }
  7. func (c *comma) Scan(state fmt.ScanState, verb rune) error {
  8. tok, err := state.Token(false, func(r rune) bool {
  9. return r != ','
  10. })
  11. if err != nil {
  12. return err
  13. }
  14. if _, _, err := state.ReadRune(); err != nil {
  15. if len(tok) == 0 {
  16. return err
  17. }
  18. }
  19. c.tok = string(tok)
  20. return nil
  21. }
  22. func main() {
  23. r := strings.NewReader("west,north,east")
  24. for {
  25. var c comma
  26. _, e := fmt.Fscan(r, &c)
  27. fmt.Printf("%q %v\n", c.tok, e)
  28. if e != nil { break }
  29. }
  30. }

结果:

  1. "west" <nil>
  2. "north" <nil>
  3. "east" <nil>
  4. "" unexpected EOF

所以结果非常接近,但是让我困扰的是unexpected EOF。是否可能只使用自定义的fmt.Scanner获得常规的EOF?我在这里做错了什么,还是这是一个错误?

  1. https://golang.org/pkg/fmt#Scanner
英文:

If I want to scan through a string, I can do this:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;strings&quot;
  5. )
  6. func main() {
  7. r := strings.NewReader(&quot;west north east&quot;)
  8. for {
  9. var s string
  10. _, e := fmt.Fscan(r, &amp;s)
  11. fmt.Printf(&quot;%q %v\n&quot;, s, e)
  12. if e != nil { break }
  13. }
  14. }

Result:

  1. &quot;west&quot; &lt;nil&gt;
  2. &quot;north&quot; &lt;nil&gt;
  3. &quot;east&quot; &lt;nil&gt;
  4. &quot;&quot; EOF

I recently discovered fmt.Scanner [1], so I thought I would try to implement
it. I came up with this:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;strings&quot;
  5. )
  6. type comma struct { tok string }
  7. func (c *comma) Scan(state fmt.ScanState, verb rune) error {
  8. tok, err := state.Token(false, func(r rune) bool {
  9. return r != &#39;,&#39;
  10. })
  11. if err != nil {
  12. return err
  13. }
  14. if _, _, err := state.ReadRune(); err != nil {
  15. if len(tok) == 0 {
  16. return err
  17. }
  18. }
  19. c.tok = string(tok)
  20. return nil
  21. }
  22. func main() {
  23. r := strings.NewReader(&quot;west,north,east&quot;)
  24. for {
  25. var c comma
  26. _, e := fmt.Fscan(r, &amp;c)
  27. fmt.Printf(&quot;%q %v\n&quot;, c.tok, e)
  28. if e != nil { break }
  29. }
  30. }

Result:

  1. &quot;west&quot; &lt;nil&gt;
  2. &quot;north&quot; &lt;nil&gt;
  3. &quot;east&quot; &lt;nil&gt;
  4. &quot;&quot; unexpected EOF

So the result is pretty close, but what bothers me is the unexpected EOF. Is
it possible to just get a regular EOF with a custom fmt.Scanner? Am I doing
something wrong here, or is this a bug?

  1. https://golang.org/pkg/fmt#Scanner

答案1

得分: 1

感谢 golang-nuts 列表上的 Ian Lance Taylor 的建议,他建议使用 panic 而不是 return 来处理错误。在 Go 代码中,Fscan 调用了一个名为 doScan 的函数,该函数又调用了一个名为 errorHandler 的函数 [1]。最后这个函数使用 recover 将任何 panic 转换为普通错误。这个程序产生了与我原始示例相同的输出:

  1. package main
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. type comma struct { tok string }
  7. func (c *comma) Scan(state fmt.ScanState, verb rune) error {
  8. tok, err := state.Token(false, func(r rune) bool {
  9. return r != ','
  10. })
  11. if err != nil { return err }
  12. if _, _, err := state.ReadRune(); err != nil {
  13. if len(tok) == 0 {
  14. panic(err)
  15. }
  16. }
  17. c.tok = string(tok)
  18. return nil
  19. }
  20. func main() {
  21. r := strings.NewReader("west,north,east")
  22. for {
  23. var c comma
  24. _, err := fmt.Fscan(r, &c)
  25. fmt.Printf("%q %v\n", c.tok, err)
  26. if err != nil { break }
  27. }
  28. }
  1. https://github.com/golang/go/blob/go1.16.4/src/fmt/scan.go#L1056-L1067
英文:

Thanks to Ian Lance Taylor on the golang-nuts list, he suggested to panic
the error instead of return. In the Go code, Fscan calls a function
doScan, which in turn calls a function errorHandler [1]. This last function
uses recover to turn any panic into regular error. This program gives
idential output to my original example:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;strings&quot;
  5. )
  6. type comma struct { tok string }
  7. func (c *comma) Scan(state fmt.ScanState, verb rune) error {
  8. tok, err := state.Token(false, func(r rune) bool {
  9. return r != &#39;,&#39;
  10. })
  11. if err != nil { return err }
  12. if _, _, err := state.ReadRune(); err != nil {
  13. if len(tok) == 0 {
  14. panic(err)
  15. }
  16. }
  17. c.tok = string(tok)
  18. return nil
  19. }
  20. func main() {
  21. r := strings.NewReader(&quot;west,north,east&quot;)
  22. for {
  23. var c comma
  24. _, err := fmt.Fscan(r, &amp;c)
  25. fmt.Printf(&quot;%q %v\n&quot;, c.tok, err)
  26. if err != nil { break }
  27. }
  28. }
  1. https://github.com/golang/go/blob/go1.16.4/src/fmt/scan.go#L1056-L1067

huangapple
  • 本文由 发表于 2021年5月27日 08:27:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/67714217.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定