为了账号安全,请及时绑定邮箱和手机立即绑定

扫描仪提前终止

扫描仪提前终止

Go
开满天机 2021-07-01 14:00:57
我正在尝试在 Go 中编写一个扫描仪,它扫描连续行并在返回之前清理该行,以便您可以返回逻辑行。因此,给定以下 SplitLine 函数 ( Play ):func ScanLogicalLines(data []byte, atEOF bool) (int, []byte, error) {    if atEOF && len(data) == 0 {        return 0, nil, nil    }    i := bytes.IndexByte(data, '\n')    for i > 0 && data[i-1] == '\\' {        fmt.Printf("i: %d, data[i] = %q\n", i, data[i])        i = i + bytes.IndexByte(data[i+1:], '\n')    }    var match []byte = nil    advance := 0    switch {    case i >= 0:        advance, match = i + 1, data[0:i]    case atEOF:         advance, match = len(data), data    }    token := bytes.Replace(match, []byte("\\\n"), []byte(""), -1)    return advance, token, nil}func main() {    simple := `Just a test.See what is returned. \when you have empty lines.Followed by a newline.`    scanner := bufio.NewScanner(strings.NewReader(simple))    scanner.Split(ScanLogicalLines)    for scanner.Scan() {        fmt.Printf("line: %q\n", scanner.Text())    }}我希望代码返回如下内容:line: "Just a test."line: ""line: "See what is returned, when you have empty lines."line: ""line: "Followed by a newline."但是,它在返回第一行后停止。第二次调用 return 1, "", nil。任何人有任何想法,还是一个错误?
查看完整描述

1 回答

?
精慕HU

TA贡献1845条经验 获得超8个赞

我认为这是一个错误,因为提前值 > 0 并不打算进行进一步的读取调用,即使返回的令牌为零(bufio.SplitFunc):

如果数据还没有一个完整的标记,例如如果它在扫描行时没有换行符,SplitFunc 可以返回 (0, nil) 以通知扫描器将更多数据读入切片并再次尝试使用更长的切片从输入中的相同点。

这是怎么回事

输入缓冲区bufio.Scanner默认为 4096 字节。这意味着如果可以,它会立即读取到这个数量,然后执行 split 函数。在您的情况下,扫描仪可以一次读取您的所有输入,因为它远低于 4096 字节。这意味着下一次读取它将导致EOF这里的主要问题。

一步步

  1. scanner.Scan 读取您的所有数据

  2. 你得到了那里的所有文本

  3. 你寻找一个令牌,你找到第一个换行符,它只有一个换行符

  4. nil通过从匹配中删除换行符作为令牌返回

  5. scanner.Scan 假设:用户需要更多数据

  6. scanner.Scan 尝试阅读更多

  7. EOF 发生

  8. scanner.Scan 尝试标记最后一次

  9. 你发现 "Just a test."

  10. scanner.Scan 尝试标记最后一次

  11. 你寻找一个标记,你会发现第三行只有一个换行符

  12. nil通过从匹配中删除换行符作为令牌返回

  13. scanner.Scan看到nil令牌并设置错误 ( EOF)

  14. 执行结束

如何规避

任何非零的令牌都会阻止这种情况。只要您返回非 nil 令牌,扫描器就不会检查EOF并继续执行您的令牌生成器。

您的代码返回nil令牌的原因是在无事可做bytes.Replace返回 。. 您可以通过返回一个有容量但没有元素的切片来防止这种情况,因为这将是非 nil: 。nilappend([]byte(nil), nil...) == nilmake([]byte, 0, 1) != nil


查看完整回答
反对 回复 2021-07-12
  • 1 回答
  • 0 关注
  • 198 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信