读文件

一次性读取整个文件

一次性读取整个文件是一种比较简单的文件读取方式。我们可以通过ioutil包中的ReadFile来读取整个文件。

我们在Golang工作目录下创建一个工程目录filehandling。然后在该工程目录下再创建一个文本文件test.txt。在该文件中输入如下文本:

Hello World. Welcome to file handling in Go.

然后创建Golang程序文件filehandling.go,并输入如下代码:

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    data, err := ioutil.ReadFile("test.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

此时的文件目录为:

src
    filehandling
        filehandling.go
        test.txt

前面程序中我们通过ioutil.ReadFile("test.txt")来读取了整个文件的内容,该函数返回的数据格式为字节切片。因此这里的data数据类型为字节切片,我们通过string(data)将字节切片转成了字符串然后再打印。该程序执行结果如下:

Contents of file: Hello World. Welcome to file handling in Go.

注意,这里我们使用的是相对目录test.txt,因此需要先切换到test.txt所在的目录,然后再执行程序,可通过如下两种方式来执行。

$ cd /home/goworkspace/src/filehandling/
$ go install filehandling
$ workspacepath/bin/filehandling
$ cd /home/goworkspace/src/filehandling/
$ go run filehandling.go

如果不切换到test.txt文件所在目录,那么执行会报错:

File reading error open test.txt: The system cannot find the file specified.

之所以会报错是因为Golang是一种编译型语言,Golang程序在执行前会先编译成二进制程序,而我们在程序中已经写死了文件目录test.txt,即二进制程序在什么目录下执行,就在哪个目录下寻找test.txt,因此如果我们不在filehandling目录下执行,则会找不到test.txt文件,就会报错。我们有三种方式能解决这个问题:

  1. 使用绝对路径

  2. 文件路径通过命令行参数动态传入

  3. 将文本文件编译到二进制程序内

这里就不细讲了,感兴趣的同学可以google下。

按字节读取

前面我们看了怎么读取整个文件,但是对于过大的文件,一次性读取到内存则不太现实。更加合理的方式是一小块一小块的读取。我们可以借助buffo包来实现。

下面我们按每3个字节一块来读取前面的test.txt文件。

package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()

    f, err := os.Open(*fptr)
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err = f.Close(); err != nil {
            log.Fatal(err)
        }
    }()
    r := bufio.NewReader(f)
    b := make([]byte, 3)
    for {
        _, err := r.Read(b)
        if err != nil {
            fmt.Println("Error reading file:", err)
            break
        }
        fmt.Println(string(b))
    }
}

这里我们从命令行中解析出了文件目录。我们defer了一个函数用来在文件读取结束后关闭文件。通过make([]byte, 3)创建了长度为3的字节切片,然后通过r.Read(b)不断的讲文件内容读入到b中。Read方法读取最多len(b)个字节,并返回真实读取到的字节数,读取到文件结尾后会返回一个EOF错误。

可通过如下方式执行该程序:

$ go install filehandling
$ wrkspacepath/bin/filehandling -fpath=/path-of-file/test.txt

执行结果如下:

Hel
lo 
Wor
ld.
 We
lco
me 
to 
fil
e h
and
lin
g i
n G
o.G
Error reading file: EOF

按行读取

下面我们看下如何按行读取文件。仍然是借助bufio实现。现在将test.txt中的文本换成如下内容。

Hello World. Welcome to file handling in Go.
This is the second line of the file.
We have reached the end of the file.

按行读取的流程如下:

  1. 打开文件

  2. 创建一个scanner

  3. 按行读取

package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()

    f, err := os.Open(*fptr)
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err = f.Close(); err != nil {
            log.Fatal(err)
        }
    }()
    s := bufio.NewScanner(f)
    for s.Scan() {
        fmt.Println(s.Text())
    }
    err = s.Err()
    if err != nil {
        log.Fatal(err)
    }
}

这里通过os.Open(*fptr)打开了文件,然后通过bufio.NewScanner(f)创建了一个scanner,然后通过s.Scan()s.Text()按行读取了文件内容。s.Err()用来判断读取文件过程中是否发生了错误。该程序执行结果如下:

Hello World. Welcome to file handling in Go.  
This is the second line of the file.  
We have reached the end of the file.

Last updated