如何在从文件中读取数据时找到EOF

25

我正在使用以下代码在Go中读取文件:

spoon , err := ioutil.ReadFile(os.Args[1])
if err!=nil {
        panic ("File reading error")
}

现在我会检查每个选取的字节是哪个字符。例如:

spoon[i]==' ' //for checking space
同样地,我读取了整个文件(我知道可能有其他读取方式),但是保持这种方式不变,我该如何知道已经到达文件的结尾并且应该停止继续读取呢?
请不要建议查找spoon的长度并开始循环。我想要一种确定的方法来找到EOF。

简单的解决方案:单片眼镜! - thang
1
@thang 什么是 Monocles? - Worlock
@Worlock:再次强调,上述问题并不直接涉及任何EOF。它只能从返回的“spoon”切片中的字节数推断出来,如果文件是顺序读取的,则会发生“io.EOF”。即使在这种设置中,io.ReadFile也可能从未看到过任何EOF。更便宜的方法是使用stat函数获取文件大小,为缓冲区(要返回的切片)分配空间,并请求操作系统完全填充它。 - zzzz
5个回答

32

使用 io.EOF 来测试文件结束。例如,要在一个文件中计算空格数:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    if len(os.Args) <= 1 {
        fmt.Println("Missing file name argument")
        return
    }
    f, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }
    defer f.Close()
    data := make([]byte, 100)
    spaces := 0
    for {
        data = data[:cap(data)]
        n, err := f.Read(data)
        if err != nil {
            if err == io.EOF {
                break
            }
            fmt.Println(err)
            return
        }
        data = data[:n]
        for _, b := range data {
            if b == ' ' {
                spaces++
            }
        }
    }
    fmt.Println(spaces)
}

2
Read可以在EOF之前返回数据:“调用者应始终在考虑错误err之前处理返回的n> 0个字节。这样做可以正确处理读取一些字节后发生的I/O错误以及两种允许的EOF行为。” - Thomas Hurst
这段代码在计数data的最后一块时跳过了空格,对吧?若文件中剩下99个空格,读取器会返回EOF错误并在不计算这最后的99个空格的情况下终止for循环。 - andrw

12

以下是关于文件结束(EOF)需要查找的内容:

if err != nil {
        if errors.Is(err, io.EOF) { // prefered way by GoLang doc
            fmt.Println("Reading file finished...")
        }
        break
    }

8

ioutil.ReadFile()方法将整个文件的内容读入一个字节切片中。你不需要关心EOF(文件结束符)。当您逐个块地读取文件时,需要EOF这一构造。在每次读取一个块时,您需要知道哪个块已到达文件结尾。

ioutil.ReadFile()返回的字节切片长度就是您所需的。

data := ioutil.ReadFile(os.Args[1])

// Do we need to know the data size?
slice_size := len(data)

// Do we need to look at each byte?
for _,byte := range data {
    // do something with each byte
}

你写道:“你需要知道哪个块已经到达了文件的结尾”。我怎么知道呢?是通过你所写的方式还是其他方式? - Worlock

3
当你使用ioutil.ReadFile()时,由于其设计原因,您永远不会看到io.EOF,因为ReadFile将读取整个文件直到达到EOF。 因此,它返回的片段整个文件。 来自文档:

ReadFile读取由filename指定的文件并返回其内容。 成功调用返回err == nil,而不是err == EOF。 由于ReadFile读取整个文件,因此它不将来自Read的EOF视为要报告的错误。

从您的问题中可以明确得知,您知道有其他读取文件的方法,并且其中一些方法需要您测试io.EOF的错误,但不需要ReadFile。
然后,使用您拥有的切片,您可以使用for ... range结构读取文件,就像其他人提到的那样。 这是一种确定性的读取整个文件且不多读的方式(再次强调,ReadFile已经处理了这一点)。 或者从0迭代到len(spoon)-1也可以,但是range更符合惯用法并基本上具有相同的功能。
换句话说:当您到达切片的末尾时,您已经到达了文件的末尾(前提是ReadFile未返回错误)。

2

切片没有文件结尾的概念。由ioutil.ReadFile返回的切片具有特定长度,反映了它所读取的文件的大小。一种常见的用法,但只是此情况下可能使用的其中一种,是对切片进行范围循环遍历,有效地“消耗”原始文件中存在的所有字节:

for i, b := range spoon {
        // At index 'i' is byte 'b'
        // At file's offset 'i', 'b' was read
        ... do something useful here
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接