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

huangapple go评论90阅读模式
英文:

How to test EOF on io.Reader in Go?

问题

Go的io.Reader文档说明,Read()方法可能同时返回非零的n值和io.EOF。不幸的是,FileRead()方法并不是这样的。

当到达文件末尾时,仍然可以读取一些字节,文件的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(&amp;r.count, uint64(n))
    return n, err
}

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

You could use this type by wrapping any reader in an eofReader:

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

    r := &amp;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 &lt;nil&gt;
// 2016/12/19 03:49:35 true &lt;nil&gt;

Code as gist.

huangapple
  • 本文由 发表于 2016年12月18日 20:33:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/41208359.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定