Go 的io.Reader文档指出 a可以同时Read()返回非零n值和 an 。io.EOF不幸的是,Read()a 的方法File并没有这样做。
当到达EOF并且仍然可以读取一些字节时,文件的Read方法返回非零n和nil错误。只有当我们尝试读取已位于文件末尾时,我们才会返回零n并io.EOF作为错误。
我找不到一种简单的方法来测试是否达到 EOF,而不尝试从文件中读取数据。如果我们使用 0 字节的缓冲区执行 Read(),尽管我们位于文件末尾,但我们会返回零n和错误。nil
为了避免最后一次读取,我找到的唯一解决方案是自己跟踪文件中剩余读取的字节数。有更简单的解决方案吗?
您可以创建一个新类型,用于跟踪迄今为止读取的字节数。然后,在 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)
}
Run Code Online (Sandbox Code Playgroud)
您可以通过将任何读取器包装在 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>
Run Code Online (Sandbox Code Playgroud)
代码作为要点。