跳转至

解决: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
}

固定大小读取数据可以更精细的控制内存,但是处理逻辑复杂可能要考虑分割数据,适用于大文件流式处理。

评论