Прочитать текстовый файл в массив строк (и написать)

101

Я считаю, что возможность читать (и записывать) текстовый файл в массив строк и из него является довольно распространенным требованием. Это также очень полезно, когда вы начинаете с языка, избавляя от необходимости изначально обращаться к базе данных. Есть ли такой на Голанге?
например

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

и

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

Я бы предпочел использовать существующий, а не дублировать.

бриано
источник
2
Используйте bufio.Scanner читать строки из файла, см stackoverflow.com/a/16615559/1136018 и golang.org/pkg/bufio
Джек Valmadre

Ответы:

125

Начиная с версии Go1.1, существует API bufio.Scanner, который может легко читать строки из файла. Рассмотрим следующий пример сверху, переписанный с помощью 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.ReadFileи strings.Splitфункции следующим образом:

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

Вы можете прочитать документацию по пакетам ioutil и strings .

yanatan16
источник
5
Он считывает весь файл в память, что может быть проблемой, если файл большой.
jergason
22
@Jergason, поэтому он начал свой ответ со слов «Если файл не слишком большой ...»
Лоран
9
ioutil можно импортировать как"io/ioutil"
Pramod
7
Note strings.Split добавит одну дополнительную строку (пустую) при разборе обычных текстовых файлов POSIX. Пример
bain
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

Вы можете использовать os.File (который реализует интерфейс io.Reader ) с bufio пакетом . Однако эти пакеты создаются с учетом фиксированного использования памяти (независимо от размера файла) и работают довольно быстро.

К сожалению, это немного усложняет чтение всего файла в память. Вы можете использовать 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()).

tux21b
источник
Спасибо за эту информацию. Меня больше интересовало использование существующего пакета для выполнения всей работы, потому что я считаю его весьма полезным. Например, я хочу использовать Go с сохранением данных без первоначального использования базы данных. Я считаю, что в некоторых языках это есть. например. Я думаю, что в Ruby есть Readlines, которые считывают массив строк (из памяти) - не то чтобы я особенно фанат Ruby. Думаю, в этом нет ничего страшного, мне просто не нравится дублирование, но, может быть, это просто я хочу этого. В любом случае, я написал для этого пакет и, возможно, выложу его на github. Эти файлы обычно очень маленькие.
brianoh 05
Если вы хотите просто сохранить любые структуры go (например, массив строк, целых чисел, карт или более сложные структуры), вы можете просто использовать gob.Encode()для этого. В результате получается двоичный файл вместо текстового файла, разделенного новой строкой. Этот файл может содержать все виды данных, его можно эффективно анализировать, итоговый файл будет меньше, и вам не придется иметь дело с этими символами новой строки и динамическим размещением. Так что это, вероятно, лучше подходит для вас, если вы просто хотите сохранить что-то для последующего использования с Go.
tux21b 05
Мне нужен массив текстовых строк, чтобы я мог изменить любую строку (поле). Эти файлы очень маленькие. При внесении изменений строки переменной длины в конечном итоге записываются обратно. Он очень гибкий и быстрый для того, что я хочу делать. Мне нужны символы новой строки для разделения строк (полей). Возможно, есть способ получше, но в настоящее время он подходит для моих целей. Я посмотрю на то, что вы предлагаете позже, и, возможно, изменю это тогда.
brianoh
2
Обратите внимание, что с версии r58 (июль 2011 г.) пакет encoding / line был удален. «Его функциональность теперь в bufio».
Кристианп
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 предстоит долгий путь ... идти ... для более общих целей. Существует МНОГО текстовых строковых данных, которые все еще очень эффективно обрабатываются на других языках и платформах. $ 0,02
ChrisH