111 votes

Lire un fichier texte dans un tableau de chaînes de caractères (et écrire)

La possibilité de lire (et d'écrire) un fichier texte dans et à partir d'un tableau de chaînes de caractères est, je crois, une exigence assez courante. C'est également très utile lorsque l'on débute avec un langage en supprimant le besoin initial d'accéder à une base de données. Est-ce que cela existe dans Golang ?
par exemple

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

et

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

Je préférerais utiliser un modèle existant plutôt que de le dupliquer.

2 votes

Utilisez bufio.Scanner pour lire les lignes d'un fichier, voir stackoverflow.com/a/16615559/1136018 y golang.org/pkg/bufio

147voto

Kyle Lemons Points 2008

A partir de la version Go1.1, il y a une bufio.Scanner API qui permet de lire facilement les lignes d'un fichier. Considérez l'exemple suivant, réécrit avec 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)
    }
}

130voto

yanatan16 Points 541

Note : ioutil est déprécié à partir de Go 1.16.

Si le fichier n'est pas trop volumineux, cela peut se faire avec la fonction ioutil.ReadFile y strings.Split fonctionne comme suit :

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

Vous pouvez lire la documentation sur ioutil y cordes paquets.

0 votes

Cette solution est beaucoup plus lente que les autres, y a-t-il des pénalités de performance ?

6 votes

Il lit l'intégralité du fichier en mémoire, ce qui peut poser un problème si le fichier est volumineux.

29 votes

@Jergason, c'est pourquoi il a commencé sa réponse par "Si le fichier n'est pas trop volumineux...".

34voto

Bill.Zhuang Points 381

Impossible de mettre à jour la première réponse.
Quoi qu'il en soit, après la sortie de Go1, il y a eu quelques changements de rupture, donc j'ai mis à jour comme indiqué ci-dessous :

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

19voto

tux21b Points 17336

Vous pouvez utiliser os.File (qui met en œuvre le io.Reader ) avec l'interface bufio pour cela. Cependant, ces paquets sont construits en tenant compte d'une utilisation fixe de la mémoire (quelle que soit la taille du fichier) et sont assez rapides.

Malheureusement, cela rend la lecture du fichier entier dans la mémoire un peu plus compliquée. Vous pouvez utiliser un octets.Buffer pour joindre les parties de la ligne si elles dépassent la limite de la ligne. Quoi qu'il en soit, je vous recommande d'essayer d'utiliser le lecteur de ligne directement dans votre projet (surtout si vous ne savez pas quelle est la taille du fichier texte !) Mais si le fichier est petit, l'exemple suivant peut être suffisant pour vous :

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

Une autre alternative pourrait être d'utiliser io.ioutil.ReadAll pour lire le fichier complet en une fois et faire le découpage par ligne ensuite. Je ne vous donne pas d'exemple explicite de la façon de réécrire les lignes dans le fichier, mais c'est en fait une façon de faire. os.Create() suivi d'une boucle similaire à celle de l'exemple (voir main() ).

0 votes

Merci pour cette information. J'étais plus intéressé par l'utilisation d'un paquet existant pour faire tout le travail, car je pense que c'est assez utile. Par exemple, je veux utiliser Go pour la persistance des données sans utiliser une base de données au départ. Je crois que certains langages disposent de cette fonctionnalité. Par exemple, je pense que Ruby dispose de Readlines qui lit un tableau de chaînes de caractères (à partir de la mémoire) - même si je ne suis pas particulièrement un fan de Ruby. Ce n'est pas un problème, je suppose, je n'aime pas la duplication, mais peut-être que c'est juste moi qui le veux. Quoi qu'il en soit, j'ai écrit un paquet pour le faire et je vais peut-être le mettre sur github. Ces fichiers sont généralement très petits.

0 votes

Si vous voulez simplement persister n'importe quel type de structures go (par exemple, un tableau de chaînes de caractères, d'entiers, de cartes ou de structures plus complexes), vous pouvez simplement utiliser la fonction gob.Encode() pour cela. Le résultat est un fichier binaire au lieu d'un fichier texte séparé par des lignes. Ce fichier peut contenir toutes sortes de données, peut être analysé efficacement, le fichier résultant sera plus petit et vous n'aurez pas à vous occuper de ces nouvelles lignes et de l'allocation dynamique. C'est donc probablement la solution la plus adaptée si vous souhaitez simplement conserver quelque chose pour une utilisation ultérieure avec Go.

0 votes

Ce que je veux, c'est un tableau de lignes de texte afin de pouvoir modifier n'importe quelle ligne (champ). Ces fichiers sont très petits. Lorsque des modifications sont apportées, les chaînes de longueur variable sont éventuellement réécrites. C'est très souple et rapide pour ce que je veux faire. J'ai besoin des retours à la ligne pour séparer les lignes (champs). Il existe peut-être une meilleure méthode, mais celle-ci semble convenir à mes besoins actuels. Je regarderai ce que vous suggérez plus tard et je le modifierai peut-être à ce moment-là.

4voto

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

ou

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

3 votes

Plus les gens essaient de dire que Go est "moderne", plus il ressemble à du code de liaison de bibliothèque vieux de 35 ans. :\Le fait que la simple lecture d'un fichier texte en ligne soit un tel désordre ne fait que renforcer le fait que Go a un long chemin à parcourir....... pour être plus polyvalent. Il y a BEAUCOUP de données textuelles, basées sur des lignes, qui sont encore traitées très efficacement dans d'autres langues et sur d'autres plateformes. $.02

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X