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

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中文件读取的相关内容,希望对您有所帮助,感谢您的阅读。

致谢

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

备注

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 35. 读取文件 文件读取是所有编程语言中最常见的操作之一。本教程我们会学习如何使用 Go 读取文件。 本教程分为...
    泷汰泱阅读 295评论 0 0
  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,313评论 0 5
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,754评论 0 5
  • 第一章 1.Linux是一套免费使用和自由传播的类UNIX操作系统,它可以基于Intel x86系列处理器以及Cy...
    yansicing阅读 5,138评论 0 9
  • 命令简介 cmd是command的缩写.即命令行 。 虽然随着计算机产业的发展,Windows 操作系统的应用越来...
    Littleston阅读 3,265评论 0 12