Почему поток кажется ПУСТЫМ после ПЕРВОГО чтения из потока с помощью bufio.Reader?
Уважаемые коллеги, почему после ПЕРВОГО чтения из потока
buf, (гдеbuf- файл или любой другой буфер, который реализуетos.Readerинтерфейс) с помощью методаbufio.NewReader(buf).ReadString('\n'), хранилищеbufстало ПУСТЫМ и во время второго чтения размер хранилища равен 0 ?Мне объяснили это так - у структуры
bufio.Reader := bufio.NewReader(buf)есть внутренний буфер по умолчанию 4096 байт и при вызове уbufio.ReaderметодаReadString('\n')в внутренний буферbufio.Readerсчитывается сразуstring1\nstring2\nто есть все строки из файла , если их размер не превышает 4096.Это нужно для Кэширования и уменьшения количества обращений к источнику данных(например файлу или , в нашем случае, байтовому потоку
buf)ВЕРНО ЛИ ЧТО так как в первом случае
func fn1()мы использовалиДВАbufio.Reader, второй ничего уже прочитать не мог - все было сохранено вбуферепервого bufio.Reader - мне объяснили так.неужели после чтения из потока ОДНИМ читателем , поток\файл ОПУСТОШАЕТСЯ ? (или в потоке\файле навсегда устанавливается какой то "курсор" на КОНЕЦ файла или на том месте до которого считал данные ПЕРВЫЙ читатель) ? Неужели при создании нескольких читателей каждый из них НЕ сможет читать с самого начала файла\потока ?
Для примера привел
func fn2()где все работает правильно, и повторное чтение уже происходит НЕ из источника данныхbuf, а из внутреннего буфера bufio.Reader, куда данные были кэшированы при прочтении ПЕРВОЙ строки
func fn1() {
buf := bytes.NewBufferString("string1\nstring2\n")
fmt.Printf("Размер буфера до чтения первой строки: %d\n", buf.Len()) // Размер буфера до чтения первой строки: 16
str1, _ = bufio.NewReader(buf).ReadString('\n')
fmt.Printf("Размер буфера после чтения первой строки: %d\n", buf.Len()) // Размер буфера после чтения первой строки: 0
str2, _ = bufio.NewReader(buf).ReadString('\n')
fmt.Println(str1, str2) // string1
}
func fn2() {
buf := bytes.NewBufferString("string1\nstring2\n")
fmt.Printf("Размер буфера до чтения первой строки: %d\n", buf.Len()) // Размер буфера до чтения первой строки: 16
r := bufio.NewReader(buf)
str1, _ = r.ReadString('\n')
fmt.Printf("Размер буфера после чтения первой строки: %d\n", buf.Len()) // Размер буфера после чтения первой строки: 0
fmt.Printf("Размер буфера bufio.Reader: %d\n", r.Buffered()) // Размер буфера bufio.Reader: 8
str2, _ = r.ReadString('\n')
fmt.Println(str1, str2) // string1 ... string2
}
Ответы (3 шт):
Вообще странно использовать две читалки одного потока.
Однако ваши рассуждения совершенно верны. Он не кажется пустым, а он опустошён на 4096 байтов.
В документации https://pkg.go.dev/bufio#NewReader сказано: Reader implements buffering for an io.Reader object.
И в исходниках https://cs.opensource.google/go/go/+/refs/tags/go1.24.3:src/bufio/bufio.go
сказано defaultBufSize = 4096
Вам надо попробовать использовать NewScanner не похоже, что он буферизует
str1, _ = bufio.NewScanner(buf).Scan()
str2, _ = bufio.NewScanner(buf).Scan()
Не моё это дело, но всё-таки зачем отдельные читалки? Второй вариант у вас - совершенно правильное использование ридера.
Когда вы отдаёте поток ридеру, вы больше не можете этим потоком распоряжаться. Ридер захватывает его и делает с ним что пожелает. Следовательно, один поток – один ридер.
Так вы можете прочитать данные два раза:
func fn3() {
data := "string1\nstring2\n" // данные файла
buf1 := bytes.NewBufferString(data) // поток
reader1 := bufio.NewReader(buf1) // читатель
str1, _ = reader1.ReadString('\n') // первая строка
buf2 := bytes.NewBufferString(data) // это второй поток
reader2 := bufio.NewReader(buf2) // это второй читатель
reader2.ReadString('\n') // пропускаем первую строку
str2, _ := reader2.ReadString('\n') // читаем вторую
fmt.Println(str1, str2) // string1 string2
}
Переменные buf1, buf2 в этом коде бесполезны. Лучше их совсем не заводить. Сразу:
reader1 := bufio.NewReader(bytes.NewBufferString(data)) // читатель
Вы будете смеяться, но для обнуления buf даже нет нужды читать из него целую строку. Достаточно прочитать один байт, и всё, буфер обнуляется.
Чтобы узнать, что происходит внутри, можно сделать свой Reader, который логирует запросы на чтение:
type MyBuffer struct {
b *bytes.Buffer
}
func (mb *MyBuffer) Read(p []byte) (n int, err error) {
fmt.Printf("Размер буфера до чтения: %d, размер целевого буфера %d\n", mb.b.Len(), len(p))
n, err = mb.b.Read(p)
if err != nil {
return n, err
}
fmt.Printf("Прочитано байт: %d\n", n)
fmt.Printf("Размер буфера после чтения: %d\n", mb.b.Len()) // Размер буфера после чтения: 0
return n, nil
}
func (mb MyBuffer) Len() int {
return mb.b.Len()
}
func NewMyBuffer(b *bytes.Buffer) *MyBuffer {
return &MyBuffer{b: b}
}
func fn3() {
buf := NewMyBuffer(bytes.NewBufferString("string1\nstring2\n"))
fmt.Printf("Размер буфера до создания читателя: %d\n", buf.Len()) // Размер буфера до создания читателя: 16
r := bufio.NewReader(buf)
fmt.Printf("Размер буфера до чтения первого байта: %d\n", buf.Len()) // Размер буфера до чтения первого байта: 16
r.ReadByte()
fmt.Printf("Размер буфера после чтения первого байта: %d\n", buf.Len()) // Размер буфера после чтения первого байта: 0
}
func main() {
fn3()
}
Результат: https://go.dev/play/p/RxpqABs_0CV
Размер буфера до создания читателя: 16
Размер буфера до чтения первого байта: 16
Размер буфера до чтения: 16, размер целевого буфера 4096
Прочитано байт: 16
Размер буфера после чтения: 0
Размер буфера после чтения первого байта: 0
Что происходит: в программе вы запросили прочитать один байт, а читатель попросил у буфера вернуть 4096 байт. Неудивительно, что bytes.Buffer обнулился - за один запрос Read он выдал всё своё содержимое.
Причина в том, что функция bufio.Reader.Read возвращает данные из своего внутреннего буфера, и если тот пуст, то пытается его заполнить, вызывая метод bufio.Reader.fill
Этот метод пытается не более 100 раз (внутренняя константа maxConsecutiveEmptyReads) прочитать что-нибудь во внутренний буфер. Если получилось, то ура, в буфере что-то появилось, и можно вернуть байт из этого буфера.
В вашем случае строка слишком короткая, поэтому буферизующий читатель зачитывает её целиком в свой внутренний буфер при первом же обращении.