如何在Go中的io.Reader上测试EOF?

时间:2016-12-18 12:33:31

标签: file go io eof

Go的io.Reader文档指出Read()可能会同时返回非零n值和io.EOF。不幸的是,Read()的{​​{1}}方法并没有这样做。

当达到EOF并且仍然可以读取某些字节时,文件的Read方法返回非零Filen错误。只有当我们尝试读取已经在文件末尾的时候,我们才会返回零niln作为错误。

我无法找到一种简单的方法来测试是否达到EOF而不尝试从文件中读取数据。如果我们使用0字节的缓冲区执行Read(),我们会返回零io.EOFn错误,尽管我们位于文件的末尾。

为避免最后一次阅读,我发现的唯一解决方案是跟踪自己在文件中读取的剩余字节数。有更简单的解决方案吗?

1 个答案:

答案 0 :(得分: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