美文网首页
Go教程第十三篇:文件读取

Go教程第十三篇:文件读取

作者: 大风过岗 | 来源:发表于2020-04-09 09:03 被阅读0次

https://golangbot.com/read-files/

读文件

读取文件是最常见的操作。在这篇文章中,我们将学习如何使用Go读文件。
这篇文章包含以下几个章节。

  • 把整个文件读进内存
    .使用文件的绝对路径
    .文件路径作为命令行参数传递
    .将文件绑定到二进制文件中

  • 一小块一小块地读取文件

  • 一行一行地读取文件

把整个文件读进内存

最基本的文件操作就是把整个文件读进内存中。使用ioutil包下的ReadFile函数即可完成这一点。我们来读取一个位于我们项目路径下的文件,先创建一个filehandling文件夹看,在这个文件夹下有一个test.txt文件看,test.txt文件中的内容是:"Hello World. Welcome to file handling in Go"

src
    filehandling
                filehandling.go
                test.txt

让我们来写段代码:

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))
}

在上面的程序中,我们读取了此文件,并返回一个字节切片data。之后,我们又把字节切片转成一个字符串,同时在控制台打印出来。
运行上面的程序,将得到如下输出结果:

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

1、使用绝对文件路径

最简单的方式就是把文件的绝对路径作为参数传递,我把刚才的程序,做了修改,使用了绝对路径。

package main

import (
    "fmt"
    "io/ioutil"
)

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

这样,程序就可以在任何地方运行,它都可以打印出text.tx的文本内容。

例如,即使在我的用户目录下,它依然可以正常运行。

$]cd $HOME
$]go install filehandling
$]workspacepath/bin/filehandling

上面的程序,将打印出test.txt文件的内容。

这种方式看上去是一种比较方便的方式,但是却有缺陷,就是文件的路径必须在程序中进行指定,否则的话,程序就会失败。

2、文件路径作为命令行参数传递

另一种方式就是,把文件的路径作为命令行标识进行传递。利用flag包,我们可以从命令行标识中读取文件路径。
我们先来理解一下,flag包的工作原理。flag包有一个String函数。这个函数会接收3个参数,第一个是标识的名字,第二个是默认值,第三个是此标识的描述。

我们来写一个小程序,我们可以从命令行标识中获取文件名称。

package main
import (
    "flag"
    "fmt"
)

func main() {
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()
    fmt.Println("value of fpath is", *fptr)
}

在上面的程序中,我们使用String函数创建了一个字符串标识fpath,同时指定了默认值test.txt,描述信息为“file path to read from”。此函数会返回字符串变量的地址。

在程序访问任何标识之前,应该先调用flag.Parse()。我们打印了此标识的值,我们使用下面的命令运行此程序:

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

我们把/path-of-file/test.txt作为命令行标识的值传递给程序。程序输出如下:

value of fpath is /path-of-file/test.txt

如果我们不传递fpath的话,程序会输出:

value of fpath is test.txt

这是因为test.txt是fpath的默认值。

现在我们知道了如何从命令行中读取文件路径,我们接下来,把程序补充完成。

package main
import (
    "flag"
    "fmt"
    "io/ioutil"
)

func main() {
    fptr := flag.String("fpath", "test.txt", "file path to read from")
    flag.Parse()
    data, err := ioutil.ReadFile(*fptr)
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

使用命令运行此程序:

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

程序的输出如下:

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

3、把text文件和二进制绑定在一起

从命令行中获取文件路径的方式虽好,但有一个比它更好的方式。我们可以把文本文件和我们的二进制程序绑定在一起。我们接下来就来试一下。

有许多包可以帮助我们完成这项工作,我们将使用packr包,因为它很简单,而且基本不出错。
第一步,安装packr包
在命令行提示符中输入如下的命令,来安装packr包。

go get -u github.com/gobuffalo/packr/...

packr包把静态文件.txt 转成.go文件,直接把文件嵌入到二进制程序中。在开发时,packers很智能,它会从磁盘上获取静态文件,而不是从二进制程序中获取文件。这样的话,在开发时如果只有静态文件改变的话,就无须进行重编译。

下面的程序会帮助我们更好地理解。

package main

import (
    "fmt"

    "github.com/gobuffalo/packr"
)

func main() {
    box := packr.NewBox("../filehandling")
    data := box.String("test.txt")
    fmt.Println("Contents of file:", data)
}

在上面的程序中,我们创建了一个New Box,box代表的是一个文件夹,文件夹中的内容会被嵌入到二进制中。在本例中,我指定的是filehandling文件夹,此文件夹中包含了一个test.txt。紧接着,我们又读取了此文件的内容,并把它打印出来。

当我们处于开发阶段时,我们可以使用go install命令来运行此命令。packr很智能,它可以从磁盘中加载文件。使用下面的命令来运行此程序:

go install filehandling
workspacepath/bin/filehandling

我们可以在任意的位置,运行此命令。packr可以根据传递给NewBox的参数,获取到此文件的绝对路径。程序将输出如下:

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

我们可以试着修改test.txt文件中的内容,并重新运行filehandling程序。这时,你可以看到程序会把更新后的文件内容输出出来而不需要重新编译程序。现在,我们来进行下一步动作,把test.txt绑定到我们的二进制上。我们会使用packr来完成此动作。

运行下面的命令:

packr install -v filehandling

程序输出如下:

building box ../filehandling
packing file filehandling.go
packed file filehandling.go
packing file test.txt
packed file test.txt
built box ../filehandling with ["filehandling.go" "test.txt"]
filehandling

此命令会把静态文件和二进制绑定在一起。

运行完上面的命令之后,再使用workspacepath/bin/filehandling命令运行此程序。程序将打印test.txt的文件内容。这时,程序就是从二进制中读取的test.txt文件内容。

如果你怀疑文件到底是不是从二进制中读取的,或者是不是从磁盘中读取的。那么你可以把test.txt删除,然后再次运行filehandling。
这时,你就会看到test.txt的内容会打印出来。完美!我们已经成功地把静态文件嵌入到我们的二进制程序中了。

一小块一小块地读取文件

在上一章节中,我们学到了如何把整个文件加载到内存中。如果当文件很大时,无法一次将整个文件加载到内存中时,尤其是当你运行在RAM上时。
另一种可取的方式就是,一小块一小块地读取文件。bufio包将帮助我们完成这一点。

我们来写一个程序,以3字节的块读取test.txt文件。用下面的内容替换filehanling.go文件:

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 {
        n, err := r.Read(b)
        if err != nil {
            fmt.Println("Error reading file:", err)
            break
        }
        fmt.Println(string(b[0:n]))
    }
}

在上面的程序中,我们首先从命令行中接收文件路径,并打开文件。
之后,我们延迟文件的关闭。同时我们又创建了一个缓冲区reader,以及一个容量为3字节的slice。 n, err := r.Read(b)方法会读取b个字节,在读取完成之后,返回所读取的字节数。我们用变量n来存储所读取的字节数。在fmt.Println(string(b[0:n]))这行代码中,会从slice中读取从脚标0到n-1的元素,并打印出来。

一旦到达文件末尾,就会返回一个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.
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、扫描此文件,并一行行地读取

我们用下面这些代码,替换filehanling.go文件中的内容:

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)
    }
}

在上面的程序中,我们接收命令行传递来的路径参数,并打开文件。然后我们用s := bufio.NewScanner(f)创建了一个新的scanner。 s.Scan()方法会读取此文件的下一行内容,并使用text方法使之转换为文本。

在Scan方法返回false之后,Err()方法将返回在读取过程中发生的任何错误。如果错误是EOF的话,Err()方法就会返回nil。
如果我们使用下面的命令,来运行此程序:

$] go install filehandling
$] workspacepath/bin/filehandling -fpath=/path-of-file/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.

以上就是Go中文件读取的相关内容,希望对您有所帮助,感谢您的阅读。

致谢

感谢您阅读本文。如果有任何反馈和问题,请您在评论区留言。

备注

本文系翻译之作原文博客地址

相关文章

网友评论

      本文标题:Go教程第十三篇:文件读取

      本文链接:https://www.haomeiwen.com/subject/ijkqmhtx.html