Leggi il file di testo nell'array di stringhe (e scrivi)


100

La capacità di leggere (e scrivere) un file di testo dentro e fuori un array di stringhe è credo un requisito abbastanza comune. È anche molto utile quando si inizia con una lingua che elimina la necessità iniziale di accedere a un database. Ne esiste uno a Golang?
per esempio

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

e

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

Preferirei usarne uno esistente piuttosto che duplicare.


2
Utilizzare bufio.Scanner per leggere le linee da un file, vedere stackoverflow.com/a/16615559/1136018 e golang.org/pkg/bufio
Jack Valmadre

Risposte:


124

A partire dalla versione Go1.1, esiste un'API bufio.Scanner che può leggere facilmente le righe da un file. Considera il seguente esempio dall'alto, riscritto con 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

Se il file non è troppo grande, questo può essere fatto con le funzioni ioutil.ReadFilee in questo strings.Splitmodo:

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

Puoi leggere la documentazione sui pacchetti ioutil e strings .


5
Legge l'intero file in memoria, il che potrebbe essere un problema se il file è di grandi dimensioni.
jergason

22
@Jergason, motivo per cui ha iniziato la sua risposta con "Se il file non è troppo grande ..."
laurent

9
ioutil può essere importato come"io/ioutil"
Pramod

7
Note stringhe: Split aggiungerà una riga in più (una stringa vuota) durante l'analisi di un normale esempio di
bain

1
Cordiali saluti, in Windows, questo non rimuoverà il file \r. Quindi potresti avere un \rallegato a ogni elemento.
matfax

32

Impossibile aggiornare la prima risposta.
Ad ogni modo, dopo il rilascio di Go1, ci sono alcune modifiche sostanziali, quindi ho aggiornato come mostrato di seguito:

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

Puoi usare os.File (che implementa l' interfaccia io.Reader ) con il pacchetto bufio per questo. Tuttavia, questi pacchetti sono creati tenendo conto dell'utilizzo fisso della memoria (indipendentemente dalle dimensioni del file) e sono abbastanza veloci.

Sfortunatamente questo rende la lettura dell'intero file nella memoria un po 'più complicata. È possibile utilizzare un byte.Buffer per unire le parti della linea se superano il limite della linea. Ad ogni modo, ti consiglio di provare ad utilizzare il line reader direttamente nel tuo progetto (soprattutto se non sai quanto è grande il file di testo!). Ma se il file è piccolo, il seguente esempio potrebbe essere sufficiente per te:

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

Un'altra alternativa potrebbe essere quella di usare io.ioutil.ReadAll per leggere il file completo in una volta e fare lo slicing per riga in seguito. Non ti do un esempio esplicito di come riscrivere le righe nel file, ma è fondamentalmente os.Create()seguito da un ciclo simile a quello nell'esempio (vedi main()).


Grazie per queste informazioni. Ero più interessato a utilizzare un pacchetto esistente per fare l'intero lavoro, perché penso che sia abbastanza utile. Ad esempio, desidero utilizzare Go con la persistenza dei dati senza utilizzare inizialmente un database. Alcune lingue hanno questo credo. per esempio. Penso che Ruby abbia Readlines che legge un array di stringhe (dalla memoria) - non che io sia particolarmente un fan di Ruby. Non è un grosso problema, immagino, semplicemente non mi piacciono i duplicati, ma forse sono solo io a volerlo. Ad ogni modo, ho scritto un pacchetto per farlo e forse lo metterò su GitHub. Questi file sono in genere molto piccoli.
brianoh

Se vuoi semplicemente persistere qualsiasi tipo di struttura go (ad esempio un array di stringhe, interi, mappe o strutture più complicate), puoi semplicemente usare gob.Encode()per quello. Il risultato è un file binario invece di un file di testo separato da una nuova riga. Questo file può contenere tutti i tipi di dati, può essere analizzato in modo efficiente, il file risultante sarà più piccolo e non dovrai occuparti di quelle nuove righe e dell'allocazione dinamica. Quindi probabilmente è più adatto a te se vuoi semplicemente persistere qualcosa per un utilizzo successivo con Go.
tux21b

Quello che voglio è un array di righe di testo in modo da poter modificare qualsiasi riga (campo). Questi file sono molto piccoli. Quando vengono apportate modifiche, le stringhe di lunghezza variabile vengono infine riscritte. È molto flessibile e veloce per quello che voglio fare. Ho bisogno delle nuove righe per separare le righe (campi). Forse esiste un modo migliore, ma al momento questo sembra OK per i miei scopi. Guarderò più tardi ciò che suggerisci e forse lo cambierò.
brianoh

2
Si noti che a partire dalla r58 (luglio 2011), il pacchetto encoding / line è stato rimosso. "La sua funzionalità è ora in Bufio."
kristianp

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

o

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
più "moderno" tutti continuano a provare a dire che Go è, più sembra solo un codice di rilegatura di librerie di 35 anni. : \ Il fatto che la semplice lettura di un file di testo basato su righe sia un tale disordine non fa che rafforzare il fatto che Go ha una lunga strada da percorrere ... per essere più generale. C'è MOLTO testo, dati basati su righe là fuori che vengono ancora elaborati in modo molto efficiente in altre lingue e piattaforme. $ 0,02
ChrisH
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.