Как читать текстовый файл по строкам в Go, если некоторые строки достаточно длинны, чтобы вызвать ошибки "bufio.Scanner: token too long"?

У меня есть текстовый файл, где каждая строка представляет объект JSON. Я обрабатываю этот файл в Go с помощью простого цикла for этого:

scanner := bufio.NewScanner(file)
for scanner.Scan() {
   jsonBytes = scanner.Bytes()
   var jsonObject interface{}
   err := json.Unmarshal(jsonBytes, &jsonObject)

   // do stuff with "jsonObject"...

}
if err := scanner.Err(); err != nil {
   log.Fatal(err)
}

Когда этот код достигает строки с особенно большой строкой JSON (~ 67kb), я получаю сообщение об ошибке "bufio.Scanner: токен слишком длинный".

Есть ли простой способ увеличить максимальный размер строки, читаемый NewScanner? Или есть другой подход, который вы можете предпринять вообще, когда нужно читать строки, которые слишком велики для NewScanner но, как известно, обычно не имеют небезопасного размера?

Ответы

Ответ 1

Из документов пакета:

Программы, которые нуждаются в большем контроле над обработкой ошибок или большими токенами или должны выполнять последовательное сканирование на считывателе, вместо этого должны использовать bufio.Reader.

Похоже, предпочтительным решением является bufio.Reader.ReadLine.

Ответ 2

Вы также можете сделать:

scanner := bufio.NewScanner(file)
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024)
for scanner.Scan() {
    // do your stuff
}

Второй аргумент для scanner.Buffer() устанавливает максимальный размер маркера. В приведенном выше примере вы сможете сканировать файл, если ни одна из строк не превышает 1 МБ.

Ответ 3

Вы, конечно же, не хотите, чтобы вы читали строки за строкой в первую очередь. Почему бы вам просто не сделать это:

d := json.NewDecoder(file)
for {
   var ob whateverType
   err := d.Decode(&ob)
   if err == io.EOF {
       break
   }
   if err != nil {
       log.Fatalf("Error decoding: %v", err)
   }

   // do stuff with "jsonObject"...

}