Respuestas:
NOTA: La respuesta aceptada fue correcta en las primeras versiones de Go. Ver la respuesta más votada contiene la forma idiomática más reciente para lograr esto.
Hay una función ReadLine en el paquete bufio
.
Tenga en cuenta que si la línea no cabe en el búfer de lectura, la función devolverá una línea incompleta. Si desea leer siempre una línea completa en su programa mediante una sola llamada a una función, deberá encapsular la ReadLine
función en su propia función que llama ReadLine
a un bucle for.
bufio.ReadString('\n')
no es totalmente equivalente a ReadLine
porque ReadString
no puede manejar el caso cuando la última línea de un archivo no termina con el carácter de nueva línea.
En Go 1.1 y versiones posteriores, la forma más sencilla de hacerlo es con a bufio.Scanner
. Aquí hay un ejemplo simple que lee líneas de un archivo:
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)
}
}
Esta es la forma más limpia de leer una Reader
línea por línea.
Hay una advertencia: el escáner no funciona bien con líneas de más de 65536 caracteres. Si eso es un problema para ti, entonces probablemente deberías rodar el tuyo encima Reader.Read()
.
file, _ := os.Open("/path/to/file.csv")
y luego escanear sobre el identificador de archivo:scanner := bufio.NewScanner(file)
defer file.Close()
.
bufio.ErrTooLong
error, que es bufio.Scanner: token too long
si la línea es demasiado larga. En cuyo caso, tendrá que usar bufio.ReaderLine () o ReadString ().
Utilizar:
reader.ReadString('\n')
\n
final de la cadena devuelta.reader.ReadLine()
Probé las diversas soluciones sugeridas al escribir un programa para probar los escenarios que se identifican como problemas en otras respuestas:
Encontre eso:
Scanner
solución no maneja largas colas.ReadLine
solución es compleja de implementar.ReadString
solución es la más simple y funciona para largas colas.Aquí hay un código que muestra cada solución, se puede ejecutar a través de 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]
}
Probé en:
El programa de prueba produce:
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()
debe ser después de la verificación de error; de lo contrario en caso de error entrará en pánico.
Escribí una forma de leer fácilmente cada línea de un archivo. La función Readln (* bufio.Reader) devuelve una línea (sans \ n) desde la estructura subyacente de bufio.Reader.
// 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
}
Puede usar Readln para leer cada línea de un archivo. El siguiente código lee cada línea de un archivo y envía cada línea 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)
}
¡Salud!
Hay dos formas comunes de leer el archivo línea por línea.
En mi caso de prueba, ~ 250MB, ~ 2,500,000 líneas , bufio.Scanner (tiempo utilizado: 0.395491384s) es más rápido que bufio.Reader.ReadString (time_used: 0.446867622s).
Código fuente: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Leer archivo 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
}
}
Leer archivo 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
ejemplo no leerá la última línea de un archivo si no termina con una nueva línea. ReadString
devolverá tanto la última línea como io.EOF
en este caso.
Ejemplo de esta esencia
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
}
}
pero esto da un error cuando hay una línea más grande que el búfer del escáner.
Cuando eso sucedió, lo que hago es usar reader := bufio.NewReader(inFile)
create y concat mi propio buffer usando ch, err := reader.ReadByte()
olen, err := reader.Read(myBuffer)
Otra forma que uso (reemplaza os.Stdin con el archivo como el anterior), este se concatena cuando las líneas son largas (isPrefix) e ignora las líneas vacías:
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
?
También puede usar ReadString con \ n como separador:
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 () funciona bien. Pero si desea leer cada línea por una cadena, intente usar ReadString ('\ n') . No necesita reinventar la rueda.
// 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
}
En el siguiente código, leo los intereses de la CLI hasta que el usuario presiona enter y estoy 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)
Me gusta la solución de Lzap, soy nuevo en Go, me gustaría pedirle a lzap pero no pude hacerlo. Todavía no tengo 50 puntos. Cambio un poco su solución y completo el código ...
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
}
}
No estoy seguro de por qué necesito probar 'err' nuevamente, pero de todos modos podemos hacerlo. Pero, la pregunta principal es ... ¿por qué Go no produce errores con la oración => línea, err: = r.ReadString (10), dentro del bucle? Se define una y otra vez cada vez que se ejecuta el bucle. Evito esa situación con mi cambio, ¿algún comentario? Establecí la condición EOF en 'for' como similar a un While también. Gracias
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Aquí hay un ejemplo con una función ReadFromStdin()
similar, fmt.Scan(&name)
pero toma todas las cadenas con espacios en blanco como: "Hola, mi nombre es ..."
var name string = ReadFromStdin()
println(name)
Otro método es usar las bibliotecas io/ioutil
y strings
para leer los bytes de todo el archivo, convertirlos en una cadena y dividirlos usando un carácter " \n
" (nueva línea) como delimitador, por ejemplo:
import (
"io/ioutil"
"strings"
)
func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
file_content := string(bytesRead)
lines := strings.Split(file_content, "\n")
}
Técnicamente no estás leyendo el archivo línea por línea, sin embargo, puedes analizar cada línea usando esta técnica. Este método es aplicable a archivos más pequeños. Si está intentando analizar un archivo masivo, use una de las técnicas que se lee línea por línea.