将文本文件读入字符串数组(并写入)


100

我认为能够将文本文件读入和写出字符串数组的能力是相当普遍的要求。从一种语言开始消除最初访问数据库的需求时,它也非常有用。Golang中是否存在?
例如

func ReadLines(sFileName string, iMinLines int) ([]string, bool) {

func WriteLines(saBuff[]string, sFilename string) (bool) { 

我宁愿使用现有的而不是重复的。


2
使用bufio.Scanner读取文件中的行,请参阅stackoverflow.com/a/16615559/1136018golang.org/pkg/bufio
Jack Valmadre

Answers:


124

从Go1.1版本开始,有一个bufio.Scanner API可以轻松读取文件中的行。考虑上面的以下示例,该示例使用Scanner重写:

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    var lines []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    return lines, scanner.Err()
}

// writeLines writes the lines to the given file.
func writeLines(lines []string, path string) error {
    file, err := os.Create(path)
    if err != nil {
        return err
    }
    defer file.Close()

    w := bufio.NewWriter(file)
    for _, line := range lines {
        fmt.Fprintln(w, line)
    }
    return w.Flush()
}

func main() {
    lines, err := readLines("foo.in.txt")
    if err != nil {
        log.Fatalf("readLines: %s", err)
    }
    for i, line := range lines {
        fmt.Println(i, line)
    }

    if err := writeLines(lines, "foo.out.txt"); err != nil {
        log.Fatalf("writeLines: %s", err)
    }
}

124

如果文件不是太大,可以使用ioutil.ReadFileand strings.Split函数来完成,如下所示:

content, err := ioutil.ReadFile(filename)
if err != nil {
    //Do something
}
lines := strings.Split(string(content), "\n")

您可以阅读有关ioutil字符串包的文档。


5
它将整个文件读入内存,如果文件很大,可能会出现问题。
杰尔加森

22
@Jergason,这就是为什么他开始了他的答案与“如果文件不是太大......”
洛朗

9
ioutil可以导入为"io/ioutil"
Pramod

7
注意string.Split在解析常规POSIX文本文件示例
bain 2014年

1
仅供参考,在Windows中,这不会删除\r。因此,您可能\r在每个元素后面都附加了一个。
matfax

32

无法更新第一个答案。
无论如何,Go1发布后,会有一些重大更改,因此我进行了如下更新:

package main

import (
    "os"
    "bufio"
    "bytes"
    "io"
    "fmt"
    "strings"
)

// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err error) {
    var (
        file *os.File
        part []byte
        prefix bool
    )
    if file, err = os.Open(path); err != nil {
        return
    }
    defer file.Close()

    reader := bufio.NewReader(file)
    buffer := bytes.NewBuffer(make([]byte, 0))
    for {
        if part, prefix, err = reader.ReadLine(); err != nil {
            break
        }
        buffer.Write(part)
        if !prefix {
            lines = append(lines, buffer.String())
            buffer.Reset()
        }
    }
    if err == io.EOF {
        err = nil
    }
    return
}

func writeLines(lines []string, path string) (err error) {
    var (
        file *os.File
    )

    if file, err = os.Create(path); err != nil {
        return
    }
    defer file.Close()

    //writer := bufio.NewWriter(file)
    for _,item := range lines {
        //fmt.Println(item)
        _, err := file.WriteString(strings.TrimSpace(item) + "\n"); 
        //file.Write([]byte(item)); 
        if err != nil {
            //fmt.Println("debug")
            fmt.Println(err)
            break
        }
    }
    /*content := strings.Join(lines, "\n")
    _, err = writer.WriteString(content)*/
    return
}

func main() {
    lines, err := readLines("foo.txt")
    if err != nil {
        fmt.Println("Error: %s\n", err)
        return
    }
    for _, line := range lines {
        fmt.Println(line)
    }
    //array := []string{"7.0", "8.5", "9.1"}
    err = writeLines(lines, "foo2.txt")
    fmt.Println(err)
}

18

您可以为此使用带有bufio包的os.File(实现io.Reader接口)。但是,这些软件包在构建时考虑了固定的内存使用情况(无论文件有多大),而且速度非常快。

不幸的是,这使得将整个文件读取到内存中变得更加复杂。如果超出了行限制,则可以使用bytes.Buffer来连接行的各个部分。无论如何,我建议您尝试直接在项目中使用行阅读器(尤其是如果不知道文本文件有多大!)。但是,如果文件很小,下面的示例可能就足够了:

package main

import (
    "os"
    "bufio"
    "bytes"
    "fmt"
)

// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err os.Error) {
    var (
        file *os.File
        part []byte
        prefix bool
    )
    if file, err = os.Open(path); err != nil {
        return
    }
    reader := bufio.NewReader(file)
    buffer := bytes.NewBuffer(make([]byte, 1024))
    for {
        if part, prefix, err = reader.ReadLine(); err != nil {
            break
        }
        buffer.Write(part)
        if !prefix {
            lines = append(lines, buffer.String())
            buffer.Reset()
        }
    }
    if err == os.EOF {
        err = nil
    }
    return
}

func main() {
    lines, err := readLines("foo.txt")
    if err != nil {
        fmt.Println("Error: %s\n", err)
        return
    }
    for _, line := range lines {
        fmt.Println(line)
    }
}

另一种选择是使用io.ioutil.ReadAll一次读取整个文件,然后按行进行切片。我没有给您一个明确的示例,说明如何将行写回到文件中,但这基本上是os.Create()一个循环,其后是一个类似于示例中的循环(请参阅参考资料main())。


感谢您提供的信息。我对使用现有程序包完成整个工作更感兴趣,因为我认为它非常有用。例如,我想在数据持久化时使用Go而不先使用数据库。我相信某些语言具有此功能。例如。我认为Ruby具有Readlines,它可以从内存中读取字符串数组-并不是我特别喜欢Ruby。我猜这没什么大不了的,我只是不喜欢重复,但也许只有我想要它。无论如何,我已经写了一个包来做,也许我会把它放在github上。这些文件通常很小。
brianoh 2011年

如果您只想保留任何类型的go结构(例如,字符串,整数,映射或更复杂的结构的数组),则可以简单地使用gob.Encode()for。结果是一个二进制文件,而不是用换行符分隔的文本文件。该文件可以包含所有类型的数据,可以被有效地解析,结果文件将更小,您不必处理这些换行符和动态分配。因此,如果您只想保留某些东西以便以后在Go中使用,它可能更适合您。
tux21b 2011年

我想要的是文本行数组,以便可以更改任何行(字段)。这些文件很小。进行更改后,可变长字符串最终将被写回。对于我想做的事情,它非常灵活,快捷。我需要换行符以分隔行(字段)。也许有更好的方法,但是就我目前的目的而言,这似乎可以。稍后我会看您的建议,然后可能会更改。
brianoh 2011年

2
请注意,从r58版(2011年7月)开始,已删除了编码/行包。“它的功能现在在bufio中。”
kristianp 2011年

4
func readToDisplayUsingFile1(f *os.File){
    defer f.Close()
    reader := bufio.NewReader(f)
    contents, _ := ioutil.ReadAll(reader)
    lines := strings.Split(string(contents), '\n')
}

要么

func readToDisplayUsingFile1(f *os.File){
    defer f.Close()
    slice := make([]string,0)

    reader := bufio.NewReader(f)

    for{

    str, err := reader.ReadString('\n')
    if err == io.EOF{
        break
    }

        slice = append(slice, str)
    }

1
每个人都试图说Go越“现代”,它看起来越像35岁的最小库绑定代码。:\仅仅读取基于行的文本文件真是一团糟,事实证明,Go有很长的路要走。仍然有很多基于文本的基于行的数据,在其他语言和平台中仍然可以非常有效地进行处理。$ .02
ChrisH '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.