string - texto - leyendo archivo línea por línea en ir
string strip_tags (12)
EDITAR: A partir de go1.1, la solución idiomática es usar bufio.Scanner
Escribí una manera de leer fácilmente cada línea de un archivo. La función Readln (* bufio.Reader) devuelve una línea (sans / n) de la estructura bufio.Reader subyacente.
// 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 en un archivo y envía cada línea a la salida estándar.
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)
}
¡Aclamaciones!
No puedo encontrar la función file.ReadLine
en Go. Puedo descubrir cómo escribir rápidamente uno, pero me pregunto si estoy pasando por alto algo aquí. ¿Cómo se lee un archivo línea por línea?
Ejemplo de esta gist
func readLine(path string) {
inFile, err := os.Open(path)
if err != nil {
fmt.Println(err.Error() + `: ` + path)
return
} else {
defer inFile.Close()
}
scanner := bufio.NewScanner(inFile)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
fmt.Println(scanner.Text()) // the line
}
}
pero esto da un error cuando hay una línea que es más grande que el búfer del escáner.
Cuando eso sucedió, lo que hago es usar reader := bufio.NewReader(inFile)
crea y concat mi propio búfer usando ch, err := reader.ReadByte()
o len, err := reader.Read(myBuffer)
En Go 1.1 y más reciente, la forma más sencilla de hacerlo es con un 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 un Reader
línea por línea.
Hay una advertencia: el escáner no maneja bien las líneas de más de 65536 caracteres. Si ese es un problema para ti, entonces probablemente deberías poner tu propio rollo encima de Reader.Read()
.
En el siguiente código, leo los intereses del CLI hasta que el usuario pulsa 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)
Hay dos maneras comunes de leer el archivo línea por línea.
- Use bufio.Scanner
- Use 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).
Código fuente: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Leer uso de archivo 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 de uso 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
}
}
Hay función ReadLine en paquete bufio
.
Tenga en cuenta que si la línea no encaja 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 función ReadLine
en su propia función que llama a ReadLine
en un ciclo 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.
Me gusta la solución Lzap, soy nuevo en Go, me gustaría preguntar por lzap, pero no pude hacerlo. Aún no tengo 50 puntos. Cambié un poco su solución y completé 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'' de nuevo, pero de todas formas podemos hacerlo. Pero, la pregunta principal es ... ¿por qué Ir no produce un error 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 a While también. Gracias
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
}
}
Utilizar:
-
reader.ReadString(''/n'')
- Si no te importa, la línea podría ser muy larga (es decir, usar una gran cantidad de RAM). Mantiene el
/n
al final de la cadena devuelta.
- Si no te importa, la línea podría ser muy larga (es decir, usar una gran cantidad de RAM). Mantiene el
-
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 4MB.
- Un archivo que no termina con un salto de línea.
Encontre eso:
- La solución del
Scanner
no maneja líneas largas. - La solución
ReadLine
es compleja de implementar. - La solución
ReadString
es la más simple y funciona para líneas largas.
Aquí hay un código que demuestra 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]
}
He probado en:
- ir versión go1.7 windows / amd64
- Ir versión go1.6.3 linux / amd64
- ir versión go1.7.4 darwin / amd64
Las salidas del programa de prueba:
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.
ReadLine funciona bien. Pero si desea leer cada línea por una cadena, intente usar ReadString(''/n'') . No es necesario 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
}
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 la función ReadFromStdin()
es como fmt.Scan(&name)
pero toma todas las cadenas con espacios en blanco como: "Hola mi nombre es ..."
var name string = ReadFromStdin()
println(name)