No puedo encontrar la file.ReadLinefunción en Go. Puedo descubrir cómo escribir uno rápidamente, pero me pregunto si estoy pasando por alto algo aquí. ¿Cómo se lee un archivo línea por línea?
A partir de Go1.1, bufio.Scanner es la mejor manera de hacerlo.
Malcolm
Respuestas:
133
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.
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 ReadLinefunción en su propia función que llama ReadLinea un bucle for.
bufio.ReadString('\n')no es totalmente equivalente a ReadLineporque ReadStringno puede manejar el caso cuando la última línea de un archivo no termina con el carácter de nueva línea.
De los documentos: "ReadLine es una primitiva de lectura de línea de bajo nivel. La mayoría de las personas que llaman deberían usar ReadBytes ('\ n') o ReadString ('\ n') o usar un escáner".
mdwhatcott
12
@mdwhatcott ¿por qué importa que sea una "primitiva de lectura de línea de bajo nivel"? ¿Cómo llega eso a la conclusión de que "La mayoría de las personas que llaman deberían usar ReadBytes ('\ n') o ReadString ('\ n') en su lugar o usar un Escáner"?
Charlie Parker
12
@CharlieParker: no estoy seguro, solo citando los documentos para agregar contexto.
mdwhatcott
11
De los mismos documentos ... "Si ReadString encuentra un error antes de encontrar un delimitador, devuelve los datos leídos antes del error y el error en sí mismo (a menudo io.EOF)". Entonces puede verificar el error io.EOF y saber que está listo.
eduncan911
1
Tenga en cuenta que una lectura o escritura puede fallar debido a una llamada interrumpida del sistema, lo que resulta en menos de la cantidad esperada de bytes leídos o escritos.
Justin Swanhart
599
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:
Esta es la forma más limpia de leer una Readerlí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().
Y dado que el OP solicitó escanear sobre un archivo, sería trivial primero file, _ := os.Open("/path/to/file.csv")y luego escanear sobre el identificador de archivo:scanner := bufio.NewScanner(file)
Evan Plumlee
14
No te olvides de hacerlo defer file.Close().
Kiril
13
El problema es Scanner.Scan () está limitado en un tamaño de búfer de 4096 [] bytes por línea. Obtendrá un bufio.ErrTooLongerror, que es bufio.Scanner: token too longsi la línea es demasiado larga. En cuyo caso, tendrá que usar bufio.ReaderLine () o ReadString ().
eduncan911
55
Solo mis $ 0.02 - esta es la respuesta más correcta en la página :)
Si no le importa que la línea pueda ser muy larga (es decir, use mucha RAM). Mantiene el \nfinal de la cadena devuelta.
reader.ReadLine()
Si le importa limitar el consumo de RAM y no le importa el trabajo adicional de manejar el caso donde la línea es mayor que el tamaño del búfer del lector.
Probé las diversas soluciones sugeridas al escribir un programa para probar los escenarios que se identifican como problemas en otras respuestas:
Un archivo con una línea de 4 MB.
Un archivo que no termina con un salto de línea.
Encontre eso:
La Scannersolución no maneja largas colas.
La ReadLinesolución es compleja de implementar.
La ReadStringsolució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 stringfor{
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.Buffervar l []bytevar isPrefix boolfor{
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, breakif 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:
Go versión go1.7 windows / amd64
go version go1.6.3 linux / amd64
go version go1.7.4 darwin / amd64
El programa de prueba produce:
Long lines
readFileWithReadString
>Read4194305 characters
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
>Read12 characters
>>Second line.
readFileWithScanner -this will fail!>Failed!: bufio.Scanner: token too long
readFileWithReadLine
>Read4194304 characters
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
>Read12 characters
>>Second line.No linebreak
readFileWithReadString
>Read28 characters
>>Doesnotendwith linebreak.
readFileWithScanner -this will fail!>Read28 characters
>>Doesnotendwith linebreak.
readFileWithReadLine
>Read28 characters
>>Doesnotendwith linebreak.
Debe verificar el error correctamente como se ve en los documentos: play.golang.org/p/5CCPzVTSj6, es decir, si err == io.EOF {break} else {return err}
Chuque
53
EDITAR: a partir de go1.1, la solución idiomática es usar bufio.Scanner
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...)}returnstring(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)}
Escribí esta respuesta antes de que saliera Go 1.1. Go 1.1 tiene un paquete de escáner en stdlib. eso proporciona la misma funcionalidad que mi respuesta. Recomendaría usar Scanner en lugar de mi respuesta ya que Scanner está en stdlib. ¡Feliz pirateo! :-)
Malcolm
30
Hay dos formas comunes de leer el archivo línea por línea.
Use bufio.Scanner
Utilice ReadString / ReadBytes / ... en bufio.Reader
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).
Tenga en cuenta que este bufio.Readerejemplo no leerá la última línea de un archivo si no termina con una nueva línea. ReadStringdevolverá tanto la última línea como io.EOFen este caso.
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:
// 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}
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
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 ..."
Otro método es usar las bibliotecas io/ioutily stringspara 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:
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.
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 llamaReadLine
a un bucle for.bufio.ReadString('\n')
no es totalmente equivalente aReadLine
porqueReadString
no puede manejar el caso cuando la última línea de un archivo no termina con el carácter de nueva línea.fuente
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: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()
.fuente
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 esbufio.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
:Probé en:
El programa de prueba produce:
fuente
defer file.Close()
debe ser después de la verificación de error; de lo contrario en caso de error entrará en pánico.EDITAR: a partir de go1.1, la solución idiomática es usar bufio.Scanner
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.
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.
¡Salud!
fuente
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,
Leer archivo use bufio.Reader,
fuente
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 comoio.EOF
en este caso.Ejemplo de esta esencia
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 usandoch, 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:
fuente
-1
?También puede usar ReadString con \ n como separador:
fuente
bufio.Reader.ReadLine () funciona bien. Pero si desea leer cada línea por una cadena, intente usar ReadString ('\ n') . No necesita reinventar la rueda.
fuente
fuente
En el siguiente código, leo los intereses de la CLI hasta que el usuario presiona enter y estoy usando Readline:
fuente
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 ...
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
fuente
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 ..."fuente
Otro método es usar las bibliotecas
io/ioutil
ystrings
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: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.
fuente