解决:bufio.Scanner: token too long 错误
我在使用 bufio.NewScanner()
读取文件时遇到了 “token too long” 错误。错误原因是 bufio.Scanner
默认限制,最大的 token
大小是 64KB
当我们单行数据超过这个限制时,会报 token too long
错误。
这其实是一个非常典型的场景,在读取非常大的数据时(比如超过几十 MB)应该采用什么方法,因为一次读取非常大的数据可能效率不高。我们首先要考虑的数据是否可以分片处理,尽可能减少对于大内存的占用。如果必须要分配大内存,也应该合理的设置缓冲区大小。
解决方案
方法 1: 增加缓冲区大小
scanner := bufio.NewScanner(r)
// 自定义缓冲区大小(例如 10MB)
const maxCapacity = 10 * 1024 * 1024 // 10MB
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity) // 关键:设置自定义缓冲区
for scanner.Scan() {
line := scanner.Text()
// 处理行数据...
}
if err := scanner.Err(); err != nil {
// 处理错误(包括token too long)
}
自定义缓冲区这种方式简单高效同时还能利用 bufio.Scanner
的行处理能力,需要预分配内存,适用于大多数场景(知道行最大长度)。
方法 2: 使用 bufio.Reader 替代 Scanner
reader := bufio.NewReader(r)
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
// 处理其他错误
}
// 处理行数据...
}
使用 bufio.Reader
没有读取长度限制内存效率高,但是需要手动处理结束符,用于读取不确定的长度。
处理非结构化的大数据
func readLargeData(r io.Reader) error {
// 使用固定大小的块读取
buf := make([]byte, 32 * 1024) // 32KB块
for {
n, err := r.Read(buf)
if n > 0 {
// 处理数据块...
}
if err != nil {
if err == io.EOF {
break
}
return err
}
}
return nil
}
固定大小读取数据可以更精细的控制内存,但是处理逻辑复杂可能要考虑分割数据,适用于大文件流式处理。