5

我正在尝试在 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

任何人有任何想法,或者它是一个错误?

4

1 回答 1

7

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

如果数据还没有保存完整的令牌,例如,如果它在扫描行时没有换行符,SplitFunc 可以返回 (0, nil) 以指示 Scanner 将更多数据读取到切片中,然后用更长的切片重试输入中的同一点。

发生的事情是这样的

默认的输入缓冲区bufio.Scanner为 4096 字节。这意味着它会立即读取此数量,然后执行拆分功能。在您的情况下,扫描仪可以一次读取您的输入,因为它远低于 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. 执行结束

如何规避

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

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

于 2013-11-12T22:16:52.857 回答