英文:
How to test EOF on io.Reader in Go?
问题
Go的io.Reader
文档说明,Read()
方法可能同时返回非零的n
值和io.EOF
。不幸的是,File
的Read()
方法并不是这样的。
当到达文件末尾时,仍然可以读取一些字节,文件的Read
方法返回非零的n
值和nil
错误。只有当我们已经在文件末尾时尝试读取时,才会返回零的n
值和io.EOF
错误。
我找不到一种简单的方法来测试是否已经到达文件末尾而不尝试从文件中读取数据。如果我们使用0字节的缓冲区执行Read()
,尽管我们已经到达文件末尾,但会返回零的n
值和nil
错误。
为了避免这最后一次读取,我唯一找到的解决办法是自己跟踪文件中剩余要读取的字节数。是否有更简单的解决办法?
英文:
The Go's io.Reader
documentation states that a Read()
may return a non zero n
value and an io.EOF
at the same time. Unfortunately, the Read()
method of a File
doesn't do that.
When the EOF is reached and some bytes could still be read, the Read method of file returns non zero n
and nil
error. It is only when we try to read when already at the end of the file that we get back zero n
and io.EOF
as error.
I couldn't find a simple method to test if the EOF is reached without trying to read data from the file. If we perform a Read() with a buffer of 0 byte, we get back zero n
and nil
error although we are at the end of file.
To avoid this last read, the only solution I have found is to keep track myself of the number of bytes remaining to read in the file. Is there a simpler solution ?
答案1
得分: 2
你可以创建一个新的类型,用于跟踪到目前为止读取的字节数。然后,在检查是否到达文件末尾的时候,你可以将实际读取的字节数与预期读取的字节数进行比较。以下是一个示例实现。eofReader
类型用于跟踪读取的字节数,并在底层类型为文件时将其与文件大小进行比较:
package main
// ... 导入语句
// eofReader 可以在没有读取操作的情况下检查是否到达文件末尾。
type eofReader struct {
r io.Reader
count uint64
}
// AtEOF 如果读取的字节数等于文件大小,则返回 true。
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 读取并计数。
func (r *eofReader) Read(buf []byte) (int, error) {
n, err := r.r.Read(buf)
atomic.AddUint64(&r.count, uint64(n))
return n, err
}
// 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())
}
// 输出:
// false <nil>
// true <nil>
代码可在 gist 上找到。
英文:
You could create a new type, that keeps track of the number of bytes read so far. Then, at EOF check time, you could compare the expected number of bytes read with the actual number of bytes read. Here is a sample implementation. The eofReader
keeps track of the number of bytes read and compares it to the file size, in case the underlying type is a file:
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)
}
You could use this type by wrapping any reader in an 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>
Code as gist.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论