Читання текстового файлу в рядковий масив (і запис)


100

Можливість читати (і записувати) текстовий файл в масив рядків і з нього - я вважаю досить поширеною вимогою. Це також досить корисно, починаючи з мови, яка спочатку потребує доступу до бази даних. Чи існує одна в Голангу?
напр

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

Відповіді:


124

Станом на випуск Go1.1, існує bufio.Scanner API, який може легко читати рядки з файлу. Розглянемо наступний приклад зверху, переписаний зі Сканером:

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 та string .


5
Він читає весь файл у пам'ять, що може бути проблемою, якщо файл великий.
jergason

22
@Jergason, саме тому він почав свою відповідь з "Якщо файл не надто великий ..."
laurent

9
ioutil можна імпортувати як"io/ioutil"
Pramod

7
Примітка strings.Split додасть одну додаткову рядок (порожній рядок) при розборі регулярних POSIX текстових файлів приклад
Бейн

1
FYI, в 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()).


Дякую за цю інформацію. Мені було більше цікаво використовувати існуючий пакет, щоб виконати всю роботу, тому що я вважаю це досить корисним. Наприклад, я хочу використовувати Go зі збереженням даних без використання бази даних спочатку. У деяких мовах це я вважаю. напр. Я думаю, що у Ruby є Readlines, який читає масив рядків (з пам'яті) - не те, що я особливо шанувальник Ruby. Думаю, це не велика справа, я просто не люблю дублювання, але, можливо, саме я цього хочу. У всякому разі, я написав пакет, щоб це зробити, і, можливо, поставлю його на github. Зазвичай цих файлів дуже мало.
бриано

Якщо ви хочете просто зберегти будь-який тип структури go (наприклад, масив рядків, цілих чисел, карт чи складніших структур), ви можете просто використовувати gob.Encode()для цього. В результаті виходить двійковий файл замість текстового файлу, розділеного новим рядком. Цей файл може містити всі види даних, їх можна ефективно аналізувати, отриманий файл буде меншим, і вам не доведеться мати справу з цими новими рядками та динамічним розподілом. Тож це, мабуть, краще для вас, якщо ви просто хочете щось наполегливо використовувати для подальшого використання Go.
tux21b

Мені потрібно - це масив текстових рядків, щоб я міг змінити будь-який рядок (поле). Цих файлів дуже мало. Після внесення змін рядки змінної довжини згодом списуються назад. Це дуже гнучко і швидко для того, що я хочу зробити. Мені потрібні нові рядки для розділення рядків (полів). Можливо, є кращий спосіб, але це здається нормальним для моїх цілей в даний час. Я перегляну те, що ви запропонуєте пізніше, і, можливо, потім це змінити.
бриано

2
Зауважте, що станом на r58 (липень 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
чим "сучаснішими" всі намагаються сказати "Іди", тим більше це виглядає, як 35-річний голий мінімальний код обов'язкової бібліотеки. : \ Те, що просто читання текстового файлу на основі рядків є таким безладом, лише підкреслює, що Go має довгий шлях до .... йти ..., щоб бути більш загальним призначенням. Є багато текстових даних на основі рядків, які все ще дуже ефективно обробляються в інших мовах та платформах. $ .02
ChrisH
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.