Je ne parviens pas à trouver la fonction file.ReadLine
dans Go. Je peux comprendre comment en écrire un rapidement, mais je me demande simplement si je ne tiens pas compte de quelque chose. Comment lire un fichier ligne par ligne?
Il y a une fonction ReadLine dans le paquet bufio
.
Veuillez noter que si la ligne ne rentre pas dans le tampon de lecture, la fonction retournera une ligne incomplète. Si vous voulez toujours lire une ligne entière de votre programme en appelant une fonction, vous devez encapsuler la fonction ReadLine
dans votre propre fonction qui appelle ReadLine
dans une boucle for.
bufio.ReadString('\n')
n'est pas totalement équivalent à ReadLine
, car ReadString
ne peut pas gérer le cas où la dernière ligne d'un fichier ne se termine pas par le caractère de nouvelle ligne.
Dans Go 1.1 et les versions plus récentes, la méthode la plus simple consiste à utiliser un bufio.Scanner
. Voici un exemple simple qui lit les lignes d'un fichier:
_package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
file, err := os.Open("/path/to/file.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
_
C'est le moyen le plus propre de lire à partir d'une Reader
ligne par ligne.
Il y a une mise en garde: le scanner ne traite pas bien les lignes de plus de 65 536 caractères. Si cela vous pose un problème, vous devriez alors probablement vous en tenir à Reader.Read()
.
Utilisation:
reader.ReadString('\n')
\n
à la fin de la chaîne renvoyée.reader.ReadLine()
J'ai testé les différentes solutions suggérées en écrivant un programme pour tester les scénarios identifiés comme des problèmes dans d'autres réponses:
Je l'ai trouvé:
Scanner
ne gère pas les longues lignes.ReadLine
est complexe à mettre en œuvre.ReadString
est la plus simple et fonctionne pour les longues lignes.Voici le code qui illustre chaque solution, il peut être exécuté via go run main.go
:
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
)
func readFileWithReadString(fn string) (err error) {
fmt.Println("readFileWithReadString")
file, err := os.Open(fn)
defer file.Close()
if err != nil {
return err
}
// Start reading from the file with a reader.
reader := bufio.NewReader(file)
var line string
for {
line, err = reader.ReadString('\n')
fmt.Printf(" > Read %d characters\n", len(line))
// Process the line here.
fmt.Println(" > > " + limitLength(line, 50))
if err != nil {
break
}
}
if err != io.EOF {
fmt.Printf(" > Failed!: %v\n", err)
}
return
}
func readFileWithScanner(fn string) (err error) {
fmt.Println("readFileWithScanner - this will fail!")
// Don't use this, it doesn't work with long lines...
file, err := os.Open(fn)
defer file.Close()
if err != nil {
return err
}
// Start reading from the file using a scanner.
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fmt.Printf(" > Read %d characters\n", len(line))
// Process the line here.
fmt.Println(" > > " + limitLength(line, 50))
}
if scanner.Err() != nil {
fmt.Printf(" > Failed!: %v\n", scanner.Err())
}
return
}
func readFileWithReadLine(fn string) (err error) {
fmt.Println("readFileWithReadLine")
file, err := os.Open(fn)
defer file.Close()
if err != nil {
return err
}
// Start reading from the file with a reader.
reader := bufio.NewReader(file)
for {
var buffer bytes.Buffer
var l []byte
var isPrefix bool
for {
l, isPrefix, err = reader.ReadLine()
buffer.Write(l)
// If we've reached the end of the line, stop reading.
if !isPrefix {
break
}
// If we're just at the EOF, break
if err != nil {
break
}
}
if err == io.EOF {
break
}
line := buffer.String()
fmt.Printf(" > Read %d characters\n", len(line))
// Process the line here.
fmt.Println(" > > " + limitLength(line, 50))
}
if err != io.EOF {
fmt.Printf(" > Failed!: %v\n", err)
}
return
}
func main() {
testLongLines()
testLinesThatDoNotFinishWithALinebreak()
}
func testLongLines() {
fmt.Println("Long lines")
fmt.Println()
createFileWithLongLine("longline.txt")
readFileWithReadString("longline.txt")
fmt.Println()
readFileWithScanner("longline.txt")
fmt.Println()
readFileWithReadLine("longline.txt")
fmt.Println()
}
func testLinesThatDoNotFinishWithALinebreak() {
fmt.Println("No linebreak")
fmt.Println()
createFileThatDoesNotEndWithALineBreak("nolinebreak.txt")
readFileWithReadString("nolinebreak.txt")
fmt.Println()
readFileWithScanner("nolinebreak.txt")
fmt.Println()
readFileWithReadLine("nolinebreak.txt")
fmt.Println()
}
func createFileThatDoesNotEndWithALineBreak(fn string) (err error) {
file, err := os.Create(fn)
defer file.Close()
if err != nil {
return err
}
w := bufio.NewWriter(file)
w.WriteString("Does not end with linebreak.")
w.Flush()
return
}
func createFileWithLongLine(fn string) (err error) {
file, err := os.Create(fn)
defer file.Close()
if err != nil {
return err
}
w := bufio.NewWriter(file)
fs := 1024 * 1024 * 4 // 4MB
// Create a 4MB long line consisting of the letter a.
for i := 0; i < fs; i++ {
w.WriteRune('a')
}
// Terminate the line with a break.
w.WriteRune('\n')
// Put in a second line, which doesn't have a linebreak.
w.WriteString("Second line.")
w.Flush()
return
}
func limitLength(s string, length int) string {
if len(s) < length {
return s
}
return s[:length]
}
J'ai testé sur:
Le programme de test produit:
Long lines
readFileWithReadString
> Read 4194305 characters
> > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
> Read 12 characters
> > Second line.
readFileWithScanner - this will fail!
> Failed!: bufio.Scanner: token too long
readFileWithReadLine
> Read 4194304 characters
> > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
> Read 12 characters
> > Second line.
No linebreak
readFileWithReadString
> Read 28 characters
> > Does not end with linebreak.
readFileWithScanner - this will fail!
> Read 28 characters
> > Does not end with linebreak.
readFileWithReadLine
> Read 28 characters
> > Does not end with linebreak.
J'ai écrit un moyen de lire facilement chaque ligne d'un fichier. La fonction Readln (* bufio.Reader) renvoie une ligne (sans\n) à partir de la structure bufio.Reader sous-jacente.
// Readln returns a single line (without the ending \n)
// from the input buffered reader.
// An error is returned iff there is an error with the
// buffered reader.
func Readln(r *bufio.Reader) (string, error) {
var (isPrefix bool = true
err error = nil
line, ln []byte
)
for isPrefix && err == nil {
line, isPrefix, err = r.ReadLine()
ln = append(ln, line...)
}
return string(ln),err
}
Vous pouvez utiliser Readln pour lire chaque ligne d’un fichier. Le code suivant lit chaque ligne d'un fichier et génère chaque ligne sur stdout.
f, err := os.Open(fi)
if err != nil {
fmt.Printf("error opening file: %v\n",err)
os.Exit(1)
}
r := bufio.NewReader(f)
s, e := Readln(r)
for e == nil {
fmt.Println(s)
s,e = Readln(r)
}
À votre santé!
Il existe deux façons courantes de lire fichier par fichier.
Dans mon test, ~ 250 Mo, ~ 2 500 000 lignes , bufio.Scanner (temps utilisé: 0,395491384s) est plus rapide que bufio.Reader.ReadString (time_used: 0,446867622s).
Code source: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Lire le fichier utiliser bufio.Scanner,
func scanFile() {
f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
if err != nil {
log.Fatalf("open file error: %v", err)
return
}
defer f.Close()
sc := bufio.NewScanner(f)
for sc.Scan() {
_ = sc.Text() // GET the line string
}
if err := sc.Err(); err != nil {
log.Fatalf("scan file error: %v", err)
return
}
}
Lire le fichier use bufio.Reader,
func readFileLines() {
f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
if err != nil {
log.Fatalf("open file error: %v", err)
return
}
defer f.Close()
rd := bufio.NewReader(f)
for {
line, err := rd.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
log.Fatalf("read file line error: %v", err)
return
}
_ = line // GET the line string
}
}
Vous pouvez également utiliser ReadString avec\n comme séparateur:
f, err := os.Open(filename)
if err != nil {
fmt.Println("error opening file ", err)
os.Exit(1)
}
defer f.Close()
r := bufio.NewReader(f)
for {
path, err := r.ReadString(10) // 0x0A separator = newline
if err == io.EOF {
// do something here
break
} else if err != nil {
return err // if you return error
}
}
Exemple de ceci Gist
func readLine(path string) {
inFile, err := os.Open(path)
if err != nil {
fmt.Println(err.Error() + `: ` + path)
return
}
defer inFile.Close()
scanner := bufio.NewScanner(inFile)
for scanner.Scan() {
fmt.Println(scanner.Text()) // the line
}
}
mais cela donne une erreur lorsqu'il existe une ligne plus grande que la mémoire tampon du scanner.
Lorsque cela s'est produit, je me suis servi de reader := bufio.NewReader(inFile)
pour créer et concaténer mon propre tampon en utilisant ch, err := reader.ReadByte()
ou len, err := reader.Read(myBuffer)
.
bufio.Reader.ReadLine () fonctionne bien. Mais si vous voulez lire chaque ligne avec une chaîne, essayez d'utiliser ReadString ('\ n') . Il n'est pas nécessaire de réinventer la roue.
// strip '\n' or read until EOF, return error if read error
func readline(reader io.Reader) (line []byte, err error) {
line = make([]byte, 0, 100)
for {
b := make([]byte, 1)
n, er := reader.Read(b)
if n > 0 {
c := b[0]
if c == '\n' { // end of line
break
}
line = append(line, c)
}
if er != nil {
err = er
return
}
}
return
}
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Voici un exemple avec la fonction ReadFromStdin()
c'est comme fmt.Scan(&name)
mais cela prend toutes les chaînes avec des espaces vides comme: "Hello My Name Is ..."
var name string = ReadFromStdin()
println(name)
Dans le code ci-dessous, j'ai lu les intérêts de la CLI jusqu'à ce que l'utilisateur appuie sur Entrée et j'utilise Readline:
interests := make([]string, 1)
r := bufio.NewReader(os.Stdin)
for true {
fmt.Print("Give me an interest:")
t, _, _ := r.ReadLine()
interests = append(interests, string(t))
if len(t) == 0 {
break;
}
}
fmt.Println(interests)
J'aime la solution Lzap, je suis nouveau dans Go, je voudrais demander à lzap mais je ne pouvais pas le faire, je n'ai pas encore 50 points .. Je change un peu votre solution et complète le code ...
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
f, err := os.Open("archiveName")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer f.Close()
r := bufio.NewReader(f)
line, err := r.ReadString(10) // line defined once
for err != io.EOF {
fmt.Print(line) // or any stuff
line, err = r.ReadString(10) // line was defined before
}
}
Je ne suis pas certain de la raison pour laquelle je dois tester à nouveau "err", mais nous pouvons le faire de toute façon. Mais la question principale est .. pourquoi Go ne produit pas d'erreur avec la phrase => ligne, err: = r.ReadString (10), à l'intérieur de la boucle? Il est défini à chaque fois que la boucle est exécutée. J'évite cette situation avec mon changement, un commentaire? Je règle la condition EOF dans le 'pour' comme semblable à un While aussi. Merci