Antworten:
HINWEIS: Die akzeptierte Antwort war in früheren Versionen von Go korrekt. Die Antwort mit der höchsten Stimmenzahl enthält den neueren idiomatischen Weg, um dies zu erreichen.
Es gibt die Funktion ReadLine im Paket bufio
.
Bitte beachten Sie, dass die Funktion eine unvollständige Zeile zurückgibt, wenn die Zeile nicht in den Lesepuffer passt. Wenn Sie immer eine ganze Zeile in Ihrem Programm durch einen einzelnen Aufruf einer Funktion lesen möchten, müssen Sie die ReadLine
Funktion in Ihre eigene Funktion kapseln, die ReadLine
in einer for-Schleife aufruft .
bufio.ReadString('\n')
ist nicht vollständig äquivalent zu, ReadLine
da ReadString
der Fall nicht behandelt werden kann, wenn die letzte Zeile einer Datei nicht mit dem Zeilenumbruchzeichen endet.
In Go 1.1 und höher ist der einfachste Weg, dies zu tun, mit a bufio.Scanner
. Hier ist ein einfaches Beispiel, das Zeilen aus einer Datei liest:
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)
}
}
Dies ist die sauberste Art, Reader
Zeile für Zeile zu lesen .
Es gibt eine Einschränkung: Der Scanner kann mit Zeilen, die länger als 65536 Zeichen sind, nicht gut umgehen. Wenn das ein Problem für Sie ist, sollten Sie wahrscheinlich Ihr eigenes darüber rollen Reader.Read()
.
file, _ := os.Open("/path/to/file.csv")
:scanner := bufio.NewScanner(file)
defer file.Close()
.
bufio.ErrTooLong
Fehlermeldung, bufio.Scanner: token too long
wenn die Leitung zu lang ist. In diesem Fall müssen Sie bufio.ReaderLine () oder ReadString () verwenden.
Verwenden:
reader.ReadString('\n')
\n
am Ende der Zeichenfolge zurückgegebene.reader.ReadLine()
Ich habe die verschiedenen Lösungsvorschläge getestet, indem ich ein Programm geschrieben habe, um die Szenarien zu testen, die in anderen Antworten als Probleme identifiziert werden:
Ich habe das gefunden:
Scanner
Lösung verarbeitet keine langen Schlangen.ReadLine
Lösung ist komplex zu implementieren.ReadString
Lösung ist die einfachste und funktioniert für lange Schlangen.Hier ist Code, der jede Lösung demonstriert und über Folgendes ausgeführt werden kann 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]
}
Ich habe getestet an:
Das Testprogramm gibt aus:
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.
defer file.Close()
sollte nach der Fehlerprüfung sein; Andernfalls gerät es bei einem Fehler in Panik.
Ich habe eine Möglichkeit geschrieben, jede Zeile einfach aus einer Datei zu lesen. Die Funktion Readln (* bufio.Reader) gibt eine Zeile (sans \ n) von der zugrunde liegenden Struktur bufio.Reader zurück.
// 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
}
Mit Readln können Sie jede Zeile aus einer Datei lesen. Der folgende Code liest jede Zeile in einer Datei und gibt jede Zeile an stdout aus.
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)
}
Prost!
Es gibt zwei gängige Methoden, um Dateien Zeile für Zeile zu lesen.
In meinem Testfall ist bufio.Scanner (verwendete Zeit: 0,395491384s) mit ~ 250 MB, ~ 2.500.000 Zeilen schneller als bufio.Reader.ReadString (time_used: 0,446867622s).
Quellcode: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Datei lesen mit 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
}
}
Datei lesen 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
}
}
bufio.Reader
Beispiel die letzte Zeile in einer Datei nicht gelesen wird, wenn sie nicht mit einer neuen Zeile endet. ReadString
gibt sowohl die letzte Zeile als auch io.EOF
in diesem Fall zurück.
Beispiel aus diesem Kern
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
}
}
Dies führt jedoch zu einem Fehler, wenn eine Zeile größer als der Puffer des Scanners ist.
Wenn das passiert ist reader := bufio.NewReader(inFile)
, benutze ich create und concat meinen eigenen Puffer entweder mit ch, err := reader.ReadByte()
oderlen, err := reader.Read(myBuffer)
Eine andere Art, die ich benutze (os.Stdin durch Datei wie oben ersetzen), ist diese, wenn lange Zeilen lang sind (isPrefix) und leere Zeilen ignoriert:
func readLines() []string {
r := bufio.NewReader(os.Stdin)
bytes := []byte{}
lines := []string{}
for {
line, isPrefix, err := r.ReadLine()
if err != nil {
break
}
bytes = append(bytes, line...)
if !isPrefix {
str := strings.TrimSpace(string(bytes))
if len(str) > 0 {
lines = append(lines, str)
bytes = []byte{}
}
}
}
if len(bytes) > 0 {
lines = append(lines, string(bytes))
}
return lines
}
-1
du erklären warum ?
Sie können ReadString auch mit \ n als Trennzeichen verwenden:
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
}
}
bufio.Reader.ReadLine () funktioniert gut. Wenn Sie jedoch jede Zeile mit einer Zeichenfolge lesen möchten, versuchen Sie, ReadString ('\ n') zu verwenden . Das Rad muss nicht neu erfunden werden.
// 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
}
Im folgenden Code lese ich die Interessen aus der CLI, bis der Benutzer die Eingabetaste drückt und ich Readline verwende:
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)
Ich mag die Lzap-Lösung, ich bin neu in Go, ich würde gerne nach lzap fragen, aber ich konnte es nicht tun. Ich habe noch keine 50 Punkte. Ich ändere ein wenig Ihre Lösung und vervollständige den 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
}
}
Ich bin mir nicht sicher, warum ich 'err' erneut testen muss, aber wir können es trotzdem tun. Die Hauptfrage ist jedoch, warum Go keinen Fehler mit dem Satz => line, err: = r.ReadString (10) innerhalb der Schleife erzeugt. Sie wird bei jeder Ausführung der Schleife immer wieder definiert. Ich vermeide diese Situation mit meiner Änderung, irgendeinen Kommentar? Ich habe die Bedingung EOF im 'für' ähnlich wie bei einer Weile eingestellt. Vielen Dank
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Hier ist ein Beispiel mit einer Funktion ReadFromStdin()
, wie sie ist, fmt.Scan(&name)
aber alle Zeichenfolgen mit Leerzeichen wie "Hallo, mein Name ist ..."
var name string = ReadFromStdin()
println(name)