【译文】go读取文件-将整个文件读入内存|Go主题月

852 阅读7分钟

【译文】
原文地址:golangbot.com/read-files/

将整个文件读入内存

最基本的文件操作之一是将整个文件读取到内存中。这是借助ioutil包的ReadFile函数完成的。

让我们阅读一个文件并打印其内容。

我通过运行filehandlingDocuments目录中创建了一个文件夹mkdir ~/Documents/filehandling

filehandling通过从filehandling目录运行以下命令来创建一个名为Go的模块。

go mod init filehandling  

我有一个文本文件test.txt,可以从我们的Go程序中读取filehandling.gotest.txt包含以下字符串

Hello World. Welcome to file handling in Go.  

这是我的目录结构。

├── Documents
│   └── filehandling
│       ├── filehandling.go
|       ├── go.mod
│       └── test.txt

让我们立即开始编写代码。创建一个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))
}

请在您的本地环境中运行此程序,因为无法在playground上读取文件。

行号 上面程序的第9行读取文件并返回存储在中的字节data。在14行我们转换data为astring并显示文件的内容。

请从存在test.txt的位置运行该程序。

如果test.txt位于**〜/ Documents / filehandling**,则使用以下步骤运行该程序,

cd ~/Documents/filehandling/  
go install  
filehandling

如果您不知道如何运行Go程序,请访问golangbot.com/hello-world…

该程序将打印:

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

例如,如果从任何其他位置运行该程序,请尝试从以下位置运行该程序 ~/Documents/

cd ~/Documents/  
filehandling

它将显示以下错误。

File reading error open test.txt: no such file or directory  

原因是Go是一种编译语言。go install做的是它会创建从源代码的二进制。二进制文件与源代码无关,可以在任何位置运行。由于test.txt找不到运行二进制文件的位置,因此程序会抱怨找不到指定的文件。

有三种方法可以解决此问题,

  1. 使用绝对文件路径
  2. 将文件路径作为命令行标志传递
  3. 将文本文件与二进制文件捆绑在一起

1.使用绝对文件路径

解决此问题的最简单方法是传递绝对文件路径。我已经修改了程序,并将路径更改为第1行中的绝对路径。请将此路径更改为您的的绝对位置test.txt

package main

import (  
    "fmt"
    "io/ioutil"
)

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

现在,该程序可以从任何位置运行,它将打印的内容test.txt

例如,即使我从主目录运行它,它也能正常工作

cd ~/Documents/filehandling  
go install  
cd ~  
filehandling  

该程序将打印内容 test.txt

这似乎是一种简单的方法,但存在一个陷阱,即文件应位于程序中指定的路径中,否则此方法将失败。

2.将文件路径作为命令行标志传递

解决此问题的另一种方法是将文件路径作为命令行参数传递。使用flag包,我们可以从命令行获取文件路径作为输入参数,然后读取其内容。

首先让我们了解一下该flag程序包是如何工作的。该flag软件包具有String 函数。此函数接受3个参数。第一个是标志的名称,第二个是默认值,第三个是标志的简短描述。

让我们编写一个小程序来从命令行读取文件名。内容替换filehandling.go为以下,

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

上面程序的第8行,使用该函数创建一个fpath带有默认值test.txt和描述的字符串标志。该函数返回存储标志值的字符串变量的地址。file path to read from``String

在程序访问任何标志之前,应调用flag.Parse()

我们在行号中打印标志的值。10

使用以下命令运行该程序时

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

我们将其/path-of-file/test.txt作为标志的值传递fpath

该程序输出

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

如果仅使用程序运行filehandling而未传递任何程序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))
}

上面的程序读取从命令行传递的文件路径的内容。使用以下命令运行该程序

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

请替换/path-of-file/为的绝对路径test.txt。例如,以我为例,我运行了命令

filehandling --fpath=/home/naveen/Documents/filehandling/test.txt  

并打印程序。

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

3.将文本文件与二进制文件捆绑在一起

从命令行获取文件路径的上述选项很好,但是有一种更好的方法可以解决此问题。如果我们能够将文本文件和二进制文件捆绑在一起,那会很棒吗?这就是我们下一步要做的。

有各种软件包可以帮助我们实现这一目标。我们将使用packr v2,因为它非常简单,并且在我的项目中一直使用它,没有任何问题。

第一步是安装packr

~/Documents/filehandling/目录中的命令提示符处键入以下命令以安装软件包

cd ~/Documents/filehandling/  
go get -u github.com/gobuffalo/packr/v2/... 

packr转换静态文件,如.txt.go其随后直接嵌入到二进制文件。Packer具有足够的智能,可以在开发过程中从磁盘而不是从二进制文件中获取静态文件。这样,仅在静态文件发生更改时,就无需在开发过程中重新编译。

一个程序将使我们更好地理解事物。内容替换filehandling.go为以下

package main

import (  
    "fmt"

    "github.com/gobuffalo/packr/v2"
)

func main() {  
        box :=  packr.New("fileBox", "../filehandling")
        data, err := box.FindString("test.txt")
        if err != nil {
            fmt.Println("File reading error", err)
                return
        }
        fmt.Println("Contents of file:", data)
}

上面程序的第10行,我们正在创建一个名为的新盒子box。一个框代表一个文件夹,其内容将嵌入二进制文件中。在这种情况下,我要指定filehandling包含的文件夹test.txt。在下一行中,我们使用FindString方法读取文件的内容并进行打印。

使用以下命令运行程序。

cd ~/Documents/filehandling  
go install  
filehandling  

程序将打印的内容test.txt

由于我们现在处于开发阶段,因此将从磁盘读取文件。尝试更改的内容,test.txt然后filehandling再次运行。您可以看到该程序test.txt 无需重新编译即可打印的更新内容。完美的 :)。

Packr还能够找到盒子的绝对路径。因此,该程序可以从任何目录运行。它不需要test.txt出现在当前目录中。cd到另一个目录,然后再次尝试运行该程序。

cd ~/Documents  
filehandling  

运行上述命令还将打印的内容test.txt

现在,我们继续下一步,并将其捆绑test.txt到我们的二进制文件中。我们使用packr2命令来执行此操作。

packr2filehandling目录运行命令。

cd ~/Documents/filehandling/  
packr2  

此命令将在源代码中搜索新框,并生成Go文件,该文件包含我们的test.txt文本文件,该文本文件已转换为字节,并且可以与Go二进制文件捆绑在一起。该命令将生成一个文件main-packr.go和一个包packrd。这两个是捆绑静态文件和二进制文件所必需的。

运行上述命令后,再次编译并运行该程序。该程序将打印的内容test.txt

go install  
filehandling  

运行时,go install您可能会收到以下错误。

build filehandling: cannot load Users/naveen/Documents/filehandling/packrd: malformed module path "Users/naveen/Documents/filehandling/packrd": missing dot in first path element  

这可能是因为packr2不知道我们正在使用Go模块。如果出现此错误,请尝试通过运行命令将go modules显式设置为on export GO111MODULE=on。将Go模块设置为打开后,必须清除并重新生成生成的文件。

packr2 clean  
packr2  
go install  
filehandling  

现在test.txt将打印的内容,并从二进制文件中读取该内容。

如果您怀疑是从二进制文件还是从磁盘提供文件,建议您删除test.txtfilehandling再次运行该命令。您可以看到test.txt的内容已打印。非常棒的:D我们已经成功地将静态文件嵌入到我们的二进制文件中。