如何将整个文件读入字符串变量中

288

我有许多小文件,不想逐行读取它们。

在Go语言中是否存在将整个文件读入字符串变量的函数?

7个回答

381
编辑ioutil包现已弃用:“弃用:自Go 1.16起,相同功能现在由io包或os包提供,并且应优先选择这些实现来编写新代码。有关详细信息,请参阅特定函数文档。”由于Go的兼容性承诺,ioutil.ReadMe是安全的,但@openwonk的更新答案对于新代码更好。
旧答案:
使用ioutil.ReadFile
func ReadFile(filename string) ([]byte, error)

ReadFile读取由文件名filename指定的文件并返回其内容。成功调用时,err == nil而不是err == EOF。因为ReadFile会读取整个文件,所以它不会将来自Read的EOF视为需要报告的错误。
如果真的有必要,可以将其转换为[]byte而不是string。
s := string(buf)

5
构建最终字符串结果时,您可以使用 append() 函数将每个文件读取的数据累积到单个字节切片中,然后将累积的字节切片转换为最终的字符串结果。或者,您也可以考虑使用 bytes.Join 函数。 - Sonia
4
请让我为您翻译:然后告诉我们如何进行转换... 这个问题并没有要求一个字节数组。 - Kyle Bridenstine
使用这个来打开一个HTML文件,我发现每一行后面都会添加一个新行,这破坏了我的一些格式。有没有什么方法可以避免这种情况? - Jonathan
Golang是强类型语言。如果你需要字符串类型而不是[]byte类型,那么转换是非常必要的。幸运的是,string(bytes)可以轻松完成这项工作。 - Rick O'Shea
6
现在已经不推荐使用Ioutil来处理文件,建议使用os模块。 - Abdulmoiz Ahmer

135

如果您只想获取内容的string,那么简单的解决方案是使用io/ioutil包中的ReadFile函数。此函数返回一个bytes切片,您可以轻松将其转换为string

Go 1.16或更新版本:

对于此示例,请将ioutil替换为os

package main

import (
    "fmt"
    "os"
)

func main() {
    b, err := os.ReadFile("file.txt") // just pass the file name
    if err != nil {
        fmt.Print(err)
    }

    fmt.Println(b) // print the content as 'bytes'

    str := string(b) // convert content to a 'string'

    fmt.Println(str) // print the content as a 'string'
}

Go 1.15或更早版本

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    b, err := ioutil.ReadFile("file.txt") // just pass the file name
    if err != nil {
        fmt.Print(err)
    }

    fmt.Println(b) // print the content as 'bytes'

    str := string(b) // convert content to a 'string'

    fmt.Println(str) // print the content as a 'string'
}

1
Ioutil现在已经被弃用了。 - Abdulmoiz Ahmer
1
感谢 @PirateKing。使用 os 包更新了答案。 - openwonk
1
谢谢@openwonk,你能把Go 1.16的示例移到答案顶部以确保它被注意到吗? - Omer Tuchfeld

25

我认为,如果你真的担心将所有这些文件连接起来的效率问题,最好的方法是将它们全部复制到同一个字节缓冲区中。

buf := bytes.NewBuffer(nil)
for _, filename := range filenames {
  f, _ := os.Open(filename) // Error handling elided for brevity.
  io.Copy(buf, f)           // Error handling elided for brevity.
  f.Close()
}
s := string(buf.Bytes())

这将打开每个文件,将其内容复制到buf中,然后关闭文件。根据您的情况,您可能实际上不需要进行转换,最后一行只是为了显示buf.Bytes()包含您要查找的数据。


嗨,io.Copy会覆盖buf的内容吗?buf的容量是多少?谢谢。 - WoooHaaaa
复制不会覆盖,它只会继续添加到缓冲区中,而缓冲区将随着需要容纳新数据而增长。 - Running Wild
1
buf具有“无限”容量。随着添加更多数据,它将继续扩展。ioutil.Readfile将分配一个足够大的缓冲区以适应完整文件,而不需要重新分配。 - Stephen Weinberg
1
使用ByteBuffer相对于简单地将其附加到切片(/数组)中,是否真的可以提高性能?内存方面呢?差异有多大? - Kissaki

19

这是我完成它的方法:

package main

import (
  "fmt"
  "os"
  "bytes"
  "log"
)

func main() {
   filerc, err := os.Open("filename")
   if err != nil{
     log.Fatal(err)
   }
   defer filerc.Close()

   buf := new(bytes.Buffer)
   buf.ReadFrom(filerc)
   contents := buf.String()

   fmt.Print(contents) 

}    

虽然ioutil.ReadFile更为简洁,因此在文件尚未打开的情况下更为可取,但是当你已经获得一个打开的文件时,buf.ReadFrom()也是一个不错的选择。 - Allen

9
您可以使用 strings.Builder:
package main

import (
   "io"
   "os"
   "strings"
)

func main() {
   f, err := os.Open("file.txt")
   if err != nil {
      panic(err)
   }
   defer f.Close()
   b := new(strings.Builder)
   io.Copy(b, f)
   print(b.String())
}

如果您不介意使用[]byte,可以使用os.ReadFile

package main
import "os"

func main() {
   b, err := os.ReadFile("file.txt")
   if err != nil {
      panic(err)
   }
   os.Stdout.Write(b)
}

根据 @Cerise Limón 的回答 建议:_"你可以通过在读取之前将 builder 扩展到文件大小来改进 strings.Builder 示例"_;在 io.Copy(b, f) 之前,我们可以使用 fi, _ := f.Stat()b.Grow(int(fi.Size())) - bartolo-otrit

1

从Go 1.16版本开始,您可以在编译时读取文件。

使用//go:embed指令和embed包(在Go 1.16中可用)。

例如:

package main

import (
    "fmt"
    _ "embed"
)

//go:embed file.txt
var s string

func main() {
    fmt.Println(s) // print the content as a 'string'
}

1
好的评论,但不是答案。 - kravemir
令人困惑的是,//go:embed 实际上不是注释。至少我不这么认为。 - PJ Brunet

-4

我不在电脑旁边,所以我写了一个草稿。你可能明白我在说什么。

func main(){
    const dir = "/etc/"
    filesInfo, e := ioutil.ReadDir(dir)
    var fileNames = make([]string, 0, 10)
    for i,v:=range filesInfo{
        if !v.IsDir() {
            fileNames = append(fileNames, v.Name())
        }
    }

    var fileNumber = len(fileNames)
    var contents = make([]string, fileNumber, 10)
    wg := sync.WaitGroup{}
    wg.Add(fileNumber)

    for i,_:=range content {
        go func(i int){
            defer wg.Done()
            buf,e := ioutil.Readfile(fmt.Printf("%s/%s", dir, fileName[i]))
            defer file.Close()  
            content[i] = string(buf)
        }(i)   
    }
    wg.Wait()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接