В этой серии в основном записаны некоторые ямки, на которые наступили во время разработки, что удобно для будущих запросов.
ямы читать построчно
Когда вам нужно прочитать файл построчно, примерный код может выглядеть так
func main() {
scanner := bufio.NewScanner(io.Reader)
for scanner.Scan() {
line := scanner.Text()
}
}
Здесь имеется в виду входной источник io.Reader
Однако недавно возникла проблема, возникшая при разработке, в соответствии с вышеупомянутым образом появляется необъяснимая плоскость, файл не был просканирован и завершается, и процесс не сообщил об ошибках; повторно проверьте код и не обнаружили проблем, подозреваемыйbufio.Scanner
Затем я перечитал документацию по Go и нашел пример на официальном сайте:
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
fmt.Println(scanner.Text()) // Println will add back the final '\n'
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
оригинальныйscanner
есть еще одинErr()
метод, официальное заявлениеErr returns the first non-EOF error that was encountered by the Scanner.
, то есть еслиscanner.Scan()
При возникновении ошибки сообщение об ошибке должно пройтиErr()
Метод Get, моя программа пошла ошибаться проигнорировала это и увидеть эту ошибку после кода, чтобы добавить полный:bufio.Scanner: token too long
затем проверьтеScan()
Исходный код, нашел следующую логику
// Is the buffer full? If so, resize.
if s.end == len(s.buf) {
// Guarantee no overflow in the multiplication below.
const maxInt = int(^uint(0) >> 1)
if len(s.buf) >= s.maxTokenSize || len(s.buf) > maxInt/2 {
s.setErr(ErrTooLong)
return false
}
newSize := len(s.buf) * 2
if newSize == 0 {
newSize = startBufSize
}
if newSize > s.maxTokenSize {
newSize = s.maxTokenSize
}
newBuf := make([]byte, newSize)
copy(newBuf, s.buf[s.start:s.end])
s.buf = newBuf
s.end -= s.start
s.start = 0
}
оригинальныйScanner
Есть настройка при инициализацииmaxTokenSize
, это значение по умолчанию равноMaxScanTokenSize = 64 * 1024
, когда длина линии больше, чем64*1024
который65536
После этого появитсяErrTooLong
эта ошибка
Также найдено в официальной документации:
Scanning stops unrecoverably at EOF, the first I/O error, or a token too large to fit in the buffer. When a scan stops, the reader may have advanced arbitrarily far past the last token. Programs that need more control over error handling or large tokens, or must run sequential scans on a reader, should use bufio.Reader instead.
Это примерно означает, что если токен слишком велик (слишком длинная строка), используйтеbufio.Reader
,ноbufio.Reader
Этой проблемы не существует?
Прочтите следующееbufio.Reader
Обнаружено, что код имеет ограничение на размер буфера, и размер буфера по умолчанию равен4096
, но есть функцияNewReaderSize
Размер этого буфера можно регулировать.
Проверятьbufio.Reader
который предоставилReadLine()
функция
ReadLine is a low-level line-reading primitive. Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner.
Общий смысл в том, что эта функция относительно низкоуровневая, рекомендуется использоватьReadBytes('\n')
илиReadString('\n')
илиScanner
, продолжить смотреть инструкции
ReadLine tries to return a single line, not including the end-of-line bytes.If the line was too long for the buffer then isPrefix is set and the beginning of the line is returned. The rest of the line will be returned from future calls. isPrefix will be false when returning the last fragment of the line. The returned buffer is only valid until the next call to ReadLine. ReadLine either returns a non-nil line or it returns an error,never both.
Отсюда мы можем видеть роль установки буфера,ReadLine
Попытается прочитать и вернуть полную строку, но если строка слишком длинная и буфер заполнен, он не вернет полную строку, а вернет содержимое буфера и установитisPrefix
заtrue
; В это время нужно продолжать звонитьReadLine
Пока вся строка не будет прочитана, внешней вызывающей программе необходимо соединить блоки вместе, чтобы сформировать полную строку. не только иметь дело сisPrefix
, но и с приставкой разбираться тоже хлопотно! Если мы активно не устанавливаем размер буфера, но предполагается, что вы должны знать длину самой длинной строки, в большинстве случаев это невозможно предсказать заранее. Не зря рекомендуется использоватьReadBytes
илиReadString
илиScanner
.
тогда см.ReadBytes
иReadString
исходник, нашелReadString
называетсяReadBytes
,иReadBytes
Проблема с размером буфера была решена
for {
var e error
frag, e = b.ReadSlice(delim)
if e == nil { // got final fragment
break
}
if e != ErrBufferFull { // unexpected error
err = e
break
}
// Make a copy of the buffer.
buf := make([]byte, len(frag))
copy(buf, frag)
full = append(full, buf)
}
по сравнению сReadLine
Быть удобным, но обязательно сделайте это разделитель PIT MARK, если ваши файлы написаны в «A \ NB \ NC \ ND», используйтеReadString
Когда вы пропустите этоd
вывод, но в последнийline
В нем все еще есть данные, поэтому вам нужно вручную судить об этом самостоятельно
Из вышеизложенного делаем вывод:
Scanner
Ограничение размера одной строки в 65536bufio.Reader
Размер буфера 4096- Если данные не являются особыми случаями, использование чтения построчно
ReadString
Наиболее удобно