2016-12-18 2 views
3

В документации Go io.Reader указано, что Read() может возвращать ненулевое значение n и io.EOF одновременно. К сожалению, метод Read()File этого не делает.Как проверить EOF на io.Reader в Go?

Когда EOF достигнут, и некоторые байты все еще могут быть прочитаны, метод Read Read возвращает ненулевое значение n и nil. Только когда мы пытаемся читать, когда уже в конце файла мы возвращаем n и io.EOF в качестве ошибки.

Я не смог найти простой способ проверить, достиг ли EOF, не пытаясь прочитать данные из файла. Если мы выполняем Read() с буфером в 0 байт, мы возвращаем ноль n и nil ошибки, хотя мы находимся в конце файла.

Чтобы избежать этого последнего сообщения, единственным решением, которое я нашел, является отслеживание количества оставшихся байтов для чтения в файле. Есть ли более простое решение?

+1

не AFAIK. вы можете использовать поиск или что-то, чтобы получить текущую позицию, но поскольку для этого потребуется больше системных вызовов, это будет медленнее. –

+3

Почему бы не сделать тип, который _allways_ читает несколько байтов и возвращает EOF? – JimB

+0

@JimB Это потрясающий трюк. Я приму это решение. Но вместо того, чтобы читать дальше, я буду следить за оставшимися байтами, чтобы я мог возвращать io.EOF как раз вовремя. Причина, по которой мне больше всего нравится, это то, что мне не нужно изменять код в моей программе. Мне нужно предоставить оболочку для файла. Если вы напишете хороший ответ с двумя вариантами, я дам вам это. – chmike

ответ

1

Вы можете создать новый тип, который отслеживает количество прочитанных байтов. Затем, во время проверки EOF, вы можете сравнить ожидаемое количество прочитанных байтов с фактическим количеством прочитанных байтов. Вот пример реализации. eofReader отслеживает количество байтов, считанных и сравнивает его с размером файла, в случае, если основной тип файла:

package main 

// ... imports 

// eofReader can be checked for EOF, without a Read. 
type eofReader struct { 
    r  io.Reader 
    count uint64 
} 

// AtEOF returns true, if the number of bytes read equals the file size. 
func (r *eofReader) AtEOF() (bool, error) { 
    f, ok := r.r.(*os.File) 
    if !ok { 
     return false, nil 
    } 
    fi, err := f.Stat() 
    if err != nil { 
     return false, err 
    } 
    return r.Count() == uint64(fi.Size()), nil 
} 

// Read reads and counts. 
func (r *eofReader) Read(buf []byte) (int, error) { 
    n, err := r.r.Read(buf) 
    atomic.AddUint64(&r.count, uint64(n)) 
    return n, err 
} 

// Count returns the count. 
func (r *eofReader) Count() uint64 { 
    return atomic.LoadUint64(&r.count) 
} 

Вы можете использовать этот тип, обернув любого читателя в eofReader:

func main() { 
    f, err := os.Open("main.go") 
    if err != nil { 
     log.Fatal(err) 
    } 

    r := &eofReader{r: f} 
    log.Println(r.AtEOF()) 

    if _, err = ioutil.ReadAll(r); err != nil { 
     log.Fatal(err) 
    } 

    log.Println(r.AtEOF()) 
} 

// 2016/12/19 03:49:35 false <nil> 
// 2016/12/19 03:49:35 true <nil> 

Код gist.