Risposte:
NOTA: la risposta accettata era corretta nelle prime versioni di Go. Vedere la risposta più votata contiene il modo idiomatico più recente per raggiungere questo obiettivo.
C'è la funzione ReadLine nel pacchetto bufio
.
Si noti che se la riga non si adatta al buffer di lettura, la funzione restituirà una riga incompleta. Se vuoi sempre leggere un'intera riga del tuo programma con una singola chiamata a una funzione, dovrai incapsulare la ReadLine
funzione nella tua funzione che chiama ReadLine
in un ciclo for.
bufio.ReadString('\n')
non è del tutto equivalente ReadLine
perché ReadString
non è in grado di gestire il caso in cui l'ultima riga di un file non termina con il carattere di nuova riga.
In Go 1.1 e versioni successive il modo più semplice per farlo è con a bufio.Scanner
. Ecco un semplice esempio che legge le righe da un file:
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)
}
}
Questo è il modo più pulito di leggere Reader
riga per riga.
C'è un avvertimento: lo scanner non gestisce bene le righe più lunghe di 65536 caratteri. Se questo è un problema per te, allora dovresti probabilmente farlo da solo Reader.Read()
.
file, _ := os.Open("/path/to/file.csv")
e poi scansionare l'handle del file:scanner := bufio.NewScanner(file)
defer file.Close()
.
bufio.ErrTooLong
errore, ovvero bufio.Scanner: token too long
se la linea è troppo lunga. In tal caso, dovrai utilizzare bufio.ReaderLine () o ReadString ().
Uso:
reader.ReadString('\n')
\n
fine della stringa restituita.reader.ReadLine()
Ho testato le varie soluzioni suggerite scrivendo un programma per testare gli scenari identificati come problemi in altre risposte:
L'ho trovato:
Scanner
soluzione non gestisce le linee lunghe.ReadLine
soluzione è complessa da implementare.ReadString
soluzione è la più semplice e funziona per le lunghe file.Ecco il codice che dimostra ogni soluzione, può essere eseguito tramite 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]
}
Ho testato su:
Il programma di test genera:
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()
dovrebbe essere dopo il controllo degli errori; altrimenti in caso di errore sarà prendere dal panico.
Ho scritto un modo per leggere facilmente ogni riga da un file. La funzione Readln (* bufio.Reader) restituisce una riga (sans \ n) dalla struttura bufio.Reader sottostante.
// 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
}
Puoi usare Readln per leggere ogni riga di un file. Il codice seguente legge ogni riga di un file e invia ciascuna riga a 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)
}
Saluti!
Esistono due modi comuni per leggere il file riga per riga.
Nel mio testcase, ~ 250 MB, ~ 2.500.000 righe , bufio.Scanner (tempo utilizzato: 0.395491384s) è più veloce di bufio.Reader.ReadString (time_used: 0.446867622s).
Codice sorgente: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Leggi il file usa 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
}
}
Leggi il file usa 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
esempio non leggerà l'ultima riga di un file se non termina con una nuova riga. ReadString
restituirà sia l'ultima riga che io.EOF
in questo caso.
Esempio da questa sintesi
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
}
}
ma questo dà un errore quando c'è una linea più grande del buffer dello scanner.
Quando ciò accade, ciò che faccio è usare reader := bufio.NewReader(inFile)
creare e concaticare il mio buffer usando ch, err := reader.ReadByte()
olen, err := reader.Read(myBuffer)
Un altro modo che uso (sostituisco os.Stdin con file come sopra), questo concatena quando le linee sono lunghe (isPrefix) e ignora le linee vuote:
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
?
Puoi anche utilizzare ReadString con \ n come separatore:
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 () funziona bene. Ma se vuoi leggere ogni riga con una stringa, prova a usare ReadString ('\ n') . Non è necessario reinventare la ruota.
// 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
}
Nel codice seguente, ho letto gli interessi dalla CLI fino a quando l'utente non preme invio e sto usando 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)
Mi piace la soluzione Lzap, sono nuovo in Go, mi piacerebbe chiedere a Lzap ma non ci sono riuscito Non ho ancora 50 punti .. Cambio un po 'la tua soluzione e compilo il codice ...
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
}
}
Non sono sicuro del motivo per cui devo testare nuovamente "err", ma in ogni caso possiamo farlo. Ma la domanda principale è ... perché Go non produce errori con la frase => linea, err: = r.ReadString (10), all'interno del ciclo? Viene definito più volte ogni volta che viene eseguito il loop. Evito quella situazione con il mio cambiamento, qualche commento? Ho impostato la condizione EOF in 'for' come simile a While. Grazie
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Ecco un esempio con la funzione ReadFromStdin()
è come fmt.Scan(&name)
ma prende tutte le stringhe con spazi vuoti come: "Ciao, il mio nome è ..."
var name string = ReadFromStdin()
println(name)
Un altro metodo consiste nell'utilizzare le librerie io/ioutil
e strings
per leggere i byte dell'intero file, convertirli in una stringa e dividerli utilizzando un carattere " \n
" (newline) come delimitatore, ad esempio:
import (
"io/ioutil"
"strings"
)
func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
file_content := string(bytesRead)
lines := strings.Split(file_content, "\n")
}
Tecnicamente non stai leggendo il file riga per riga, tuttavia puoi analizzare ogni riga usando questa tecnica. Questo metodo è applicabile a file più piccoli. Se stai tentando di analizzare un file di grandi dimensioni, utilizza una delle tecniche che legge riga per riga.