golang教程之读取文件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wyy626562203/article/details/83411759

读取文件

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

在这里插入图片描述

将整个文件读入内存

最基本的文件操作之一是将整个文件读入内存。 这是在ioutil包的ReadFile函数的帮助下完成的。

让我们从go程序所在的目录中读取一个文件。 我已经在我的GOROOT内部创建了一个文件夹filehandling ,我在里面有一个文本文件test.txt,它将使用我们的Go程序filehandling.go来读取。 test.txt包含文本“Hello World,欢迎使用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))
}

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

上述程序第9行中读取文件并返回存储在数据中的字节片。我们将数据转换为字符串并显示文件的内容。

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

例如,在linux/mac的情况下,如果test.txt位于home/naveen/go/src/filehandling,则使用以下步骤运行该程序,

$]cd /home/naveen/go/src/filehandling/
$]go install filehandling
$]workspacepath/bin/filehandling

对于windows,如果test.txt位于C:\Users\naveen.r\go\src\filehandling,则使用以下步骤运行此程序,

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

如果从任何其他位置运行此程序,例如尝试从/home/userdirectory运行该程序,它将打印以下错误。

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

原因是Go是一种编译语言。 安装的作用是,它从源代码创建二进制文件。 二进制文件独立于源代码,可以从任何位置运行。 由于在运行二进制文件的位置找不到test.txt,程序会报错它无法找到指定的文件。

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

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

让我们一个一个讨论。

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

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

例如,即使我从我的主目录运行它也会工作

$]cd $HOME
$]go install filehandling
$]workspacepath/bin/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,并使用String函数读取描述文件路径。 此函数返回存储标志值的字符串变量的地址。

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

我们在第一行打印标志的值。

使用该命令运行此程序时

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

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

该程序输出

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  

请将/path-of-file/替换为test.txt的实际路径。 该程序将打印

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

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

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

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

第一步是安装packr包。

在命令提示符中键入以下命令以安装该程序包

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

packr将静态文件(如.txt)转换为.go文件,然后直接嵌入到二进制文件中。 Packer非常智能,可以在开发过程中从磁盘而不是从二进制文件中获取静态文件。 这样可以防止在只有静态文件发生变化时需要在开发期间重新编译。

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

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

在上面的第10行程序,我们正在创建一个新盒子。盒子表示一个文件夹,其内容将嵌入到二进制文件中。 在这种情况下,我指定包含test.txtfilehandling文件夹。 在下一行中,我们读取文件的内容并打印出来。

当我们处于开发阶段时,我们可以使用go install命令来运行该程序。 它将按预期工作。 packr足够智能,可以在开发阶段从磁盘加载文件。

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

go install filehandling  
workspacepath/bin/filehandling  

这些命令可以从任何位置运行。 Packr足够智能,可以获取传递给NewBox命令的目录的绝对路径。

这个程序将打印出来

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

尝试更改test.txt的内容并再次运行文件处理。 您可以看到该程序打印test.txt的更新内容,而无需任何重新编译。完美。

现在让我们将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并再次运行命令文件句柄。 您可以看到test.txt的内容已打印出来。 太棒了,我们已成功将静态文件嵌入到二进制文件中。

以小块读取文件

在上一节中,我们学习了如何将整个文件加载到内存中。 当文件的大小非常大时,将整个文件读入内存是没有意义的,尤其是在RAM不足的情况下。 更优化的方法是以小块读取文件。 这可以在bufio包的帮助下完成。

让我们编写一个程序,以3个字节的块为单位读取test.txt文件。 用以下内容替换filehandling.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 {
        _, err := r.Read(b)
        if err != nil {
            fmt.Println("Error reading file:", err)
            break
        }
        fmt.Println(string(b))
    }
}

在上面的程序中,我们使用从命令行标志传递的路径打开文件。

在第19行我们延迟关闭文件。

上面的程序第24行中创建了一个新的缓冲读取器。 在下一行中,我们创建一个长度和容量为3的字节片,文件的字节将被读入其中。

第27行中的Read方法读取len(b)字节,即最多3个字节,并返回读取的字节数。 一旦到达文件末尾,它将返回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  

逐行读取文件

在本节中,我们将讨论如何使用Go逐行读取文件。 这可以使用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.

以下是逐行读取文件所涉及的步骤。

  • 打开文件
  • 从文件中创建一个新的扫描程序
  • 扫描文件并逐行读取。

用以下内容替换filehandling.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)
    }
}

在上面的程序中,我们使用从命令行标志传递的路径打开文件。在第24行我们使用该文件创建一个新的扫描器。第25行为scan()的方法。通过text()方法可读取文件的下一行。

Scan返回false之后,Err()方法将返回扫描期间发生的任何错误,除非文件读取结束,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.

猜你喜欢

转载自blog.csdn.net/wyy626562203/article/details/83411759