ChatGPT解决这个技术问题 Extra ChatGPT

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

我有很多小文件,我不想逐行阅读它们。

Go中有一个函数可以将整个文件读入字符串变量吗?


T
Tim Cooper

使用 ioutil.ReadFile

func ReadFile(filename string) ([]byte, error)

ReadFile 读取由 filename 命名的文件并返回内容。成功的调用返回 err == nil,而不是 err == EOF。因为 ReadFile 读取整个文件,所以它不会将 Read 中的 EOF 视为要报告的错误。

您将获得 []byte 而不是 string。如果真的有必要,它可以转换:

s := string(buf)

然后为了构造最终的字符串结果,您可以在读取每个文件时使用 append() 将数据累积在单个字节切片中,然后将累积的字节切片转换为最终的字符串结果。或者,您可能喜欢 bytes.Join。
然后向我们展示如何转换它......问题不要求字节数组。
使用它打开一个 html 文件,我发现在每一行之后都附加了一个新行,这让我们的一些格式变得混乱。有没有办法避免这种情况?
golang 是强类型的。如果您需要类型字符串而不是 []byte,则转换是非常必要的。很高兴 string(bytes) 完成了工作。
现在我们不推荐使用 Ioutil 来处理文件。
o
openwonk

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

转到 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'
}

转到 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'
}

Ioutil 现在已弃用
谢谢@海盗王。使用 os 包更新了答案。
感谢@openwonk,您能否将 Go 1.16 示例移到答案的顶部以确保它被注意到?
R
Running Wild

如果您真的关心连接所有这些文件的效率,我认为最好的办法是将它们全部复制到相同的字节缓冲区中。

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 的容量是多少?谢谢。
复制不会覆盖,它只会继续添加到 buf,并且 buf 将根据需要增长以容纳新数据。
buf 具有“无限”容量。随着更多数据的添加,它将继续扩展。 ioutil.Readfile 将分配一个足够大的缓冲区以容纳整个文件并且不需要重新分配。
与简单地将其附加到切片(/数组)相比,使用字节缓冲区真的可以提高性能吗?内存呢?差别有多大?
R
Raghav Dinesh

我是这样做的:

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() 在您已经获得打开文件的情况下有效,因此这是一个很好的第二个答案。
N
Nimantha

您可以使用 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 的回答suggests“您可以通过在读取之前将构建器增大到文件大小来改进 strings.Builder 示例”;我们可以在 io.Copy(b, f) 之前 fi, _ := f.Stat() b.Grow(int(fi.Size()))
f
fwhez

我不会用电脑,所以我写了一个草稿。你可能对我说的很清楚。

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