我正在尝试在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."
然而,它在返回第一行后停止。第二个调用返回1, "", nil
。
任何人都有任何想法,或者它是一个错误?
答案 0 :(得分:6)
我认为这是一个bug,因为提前值> 0 即使返回的令牌为零(bufio.SplitFunc),也不打算进行进一步的读取调用:
如果数据尚未保存完整的令牌,例如,如果在扫描线时没有换行符,则SplitFunc可以返回(0,nil)以通知扫描器将更多数据读入切片并再次尝试更长时间切片从输入中的同一点开始。
bufio.Scanner
的输入缓冲区默认为4096字节。这意味着它读到了这一点
如果可以,则立即执行,然后执行拆分功能。在您的情况下,扫描仪可以一次读取您的输入,因为它远低于4096字节。 这意味着下次阅读会导致EOF
结果,这是主要问题。
scanner.Scan
reads all your data nil
scanner.Scan
assumes: user needs more data scanner.Scan
attempts to read more EOF
happens scanner.Scan
tries to tokenize one last time "Just a test."
scanner.Scan
tries to tokenize one last time nil
scanner.Scan
sees nil
token and set error (EOF
) 任何非零的令牌都会阻止这种情况发生。只要你返回非零令牌即可
扫描程序不会检查EOF
并继续执行您的标记生成器。
您的代码返回nil
令牌的原因是bytes.Replace
返回
有nothing to be done的时候nil
。 append([]byte(nil), nil...) == nil
。
您可以通过返回具有容量但没有元素的切片来防止这种情况
这将是非零的:make([]byte, 0, 1) != nil
。