Голан, читаемый из трубы, читает тонны данных

Я пытаюсь прочитать архив, который tarred, streaming, для stdin, но я как-то читаю гораздо больше данных в трубе, чем отправляет tar.

Я запускаю свою команду следующим образом:

tar -cf - somefolder | ./my-go-binary

Исходный код выглядит следующим образом:

package main

import (
    "bufio"
    "io"
    "log"
    "os"
)

// Read from standard input
func main() {
    reader := bufio.NewReader(os.Stdin)
    // Read all data from stdin, processing subsequent reads as chunks.
    parts := 0
    for {
        parts++
        data := make([]byte, 4<<20) // Read 4MB at a time
        _, err := reader.Read(data)
        if err == io.EOF {
            break
        } else if err != nil {
            log.Fatalf("Problems reading from input: %s", err)
        }
    }
    log.Printf("Total parts processed: %d\n", parts)
}

Для 100MB tarred-папки я получаю 1468 кусков 4MB (это 6.15GB)! Кроме того, не имеет значения, насколько велик массив data []byte: если я задаю размер блока до 40 МБ, я все равно получаю ~ 1400 кусков 40 МБ данных, что не имеет никакого смысла.

Есть ли что-то, что мне нужно сделать для правильного чтения данных из os.Stdin с помощью Go?

Ответ 1

Ваш код неэффективен. Он каждый раз выделяет и инициализирует data через цикл.

for {
    data := make([]byte, 4<<20) // Read 4MB at a time
}

Код для reader как io.Reader неверен. Например, вы игнорируете количество байтов, считанных с помощью _, err := reader.Read(data), и вы неправильно обрабатываете ошибки err.

Пакет io

import "io" 

введите Reader

type Reader interface {
        Read(p []byte) (n int, err error)
}

Reader - это интерфейс, который обертывает базовый метод чтения.

Чтение считывает до len (p) байтов в p. Он возвращает количество байтов read (0 <= n <= len (p)) и любая возникшая ошибка. Даже если Read возвращает n < len (p), он может использовать все p как место для царапин во время вызов. Если доступны некоторые данные, но не len (p) байты, Read условно возвращает то, что доступно, а не ждет больше.

Когда Read обнаруживает ошибку или состояние конца файла после успешно считывая n > 0 байтов, он возвращает количество прочитанных байтов. Он может возвращать (не-ноль) ошибку из одного и того же вызова или возвращать error (и n == 0) из последующего вызова. Экземпляр этого общего Дело в том, что читатель возвращает ненулевое число байтов в конце входного потока может возвращать либо err == EOF, либо err == nil. next Read должен возвращать 0, EOF независимо.

Звонящие должны всегда обрабатывать n > 0 байт, возвращенных до учитывая ошибку err. Это правильно обрабатывает ошибки ввода-вывода, которые происходят после прочтения некоторых байтов, а также обоих разрешенных EOF поведения.

Реализации Read не рекомендуется возвращать нулевой байт count с ошибкой nil, за исключением случаев, когда len (p) == 0. Звонящие должны лечить возврат 0 и ноль, указывающий на то, что ничего не произошло; в в частности, это не указывает на EOF.

Реализации не должны сохраняться.

Здесь программа чтения файла модели, соответствующая интерфейсу io.Reader:

package main

import (
    "bufio"
    "io"
    "log"
    "os"
)

func main() {
    nBytes, nChunks := int64(0), int64(0)
    r := bufio.NewReader(os.Stdin)
    buf := make([]byte, 0, 4*1024)
    for {
        n, err := r.Read(buf[:cap(buf)])
        buf = buf[:n]
        if n == 0 {
            if err == nil {
                continue
            }
            if err == io.EOF {
                break
            }
            log.Fatal(err)
        }
        nChunks++
        nBytes += int64(len(buf))
        // process buf
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }
    }
    log.Println("Bytes:", nBytes, "Chunks:", nChunks)
}

Вывод:

2014/11/29 10:00:05 Bytes: 5589891 Chunks: 1365

Ответ 2

Прочитайте документацию для чтения:

Чтение чтения данных на стр. Он возвращает количество байтов, считанных в p. Это звонки. Прочитайте не более одного раза на базовом Reader, следовательно, n может быть меньше чем len (p). В EOF счетчик будет равен нулю, а err будет равен io.EOF.

Вы не читаете 4 МБ за раз. Вы предоставляете буферное пространство и отбрасываете целое число, которое должно было бы сказать вам, сколько Чтение действительно читается. Буферное пространство является максимальным, но, как правило, обычно 128 тыс. Читается за звонок, по крайней мере, в моей системе. Попробуйте сами:

// Read from standard input
func main() {
    reader := bufio.NewReader(os.Stdin)
    // Read all data from stdin, passing the data as parts into the channel
    // for processing.
    parts := 0
    for {
        parts++
        data := make([]byte, 4<<20) // Read 4MB at a time
        amount , err := reader.Read(data)
        // WILL NOT BE 4MB!
        log.Printf("Read: %v\n", amount)
        if err == io.EOF {
            break
        } else if err != nil {
            log.Fatalf("Problems reading from input: %s", err)
        }
    }
    log.Printf("Total parts processed: %d\n", parts)
}

Вы должны реализовать логику обработки различных значений чтения.