dongshengyin0147
2016-12-18 12:33
浏览 460
已采纳

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

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 ?

图片转代码服务由CSDN问答提供 功能建议

Go的 io.Reader 文档指出, Read()可能会同时返回非零的 n 值和 io.EOF 。 不幸的是, File Read()方法不能做到这一点。

达到EOF并且仍然可以读取某些字节时,文件的Read方法返回非零的 n nil 错误 。 只有当我们尝试读取文件末尾的内容时,我们才将零 n io.EOF 返回为错误。

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

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

  • 写回答
  • 好问题 提建议
  • 追加酬金
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • dongtuo4723 2016-12-19 02:51
    已采纳

    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.

    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题