Leyendo un archivo línea por línea en Go: de principiante a experto en 2025
\n\n¿Alguna vez te has preguntado cómo leer eficientemente archivos de registro enormes o procesar datos CSV en Go? Si es así, has llegado al lugar correcto. El tema de leer un archivo línea por línea es uno de los más populares en Stack Overflow, y no es casualidad. Go nos brinda herramientas potentes, pero elegir el enfoque adecuado a veces no es fácil.
\n\nEn este artículo no solo mostraremos cómo leer líneas de un archivo. Analizaremos las mejores prácticas de 2025: desde el rápido bufio.Scanner hasta soluciones con búfer personalizado. Aprenderás cómo evitar trampas (por ejemplo, líneas de más de 64 KB), manejar errores correctamente y acelerar el trabajo con grandes volúmenes de datos.
¿Listo? ¡Vamos allá!
\n\n¿Por qué leer líneas no es simplemente "tomar y leer"?
\n\nEn Go hay varias formas de leer archivos, y cada una es para una tarea específica. Algunos usan ioutil.ReadFile (por cierto, desde Go 1.16 está obsoleto), otros usan os.File con un búfer. Pero para el procesamiento línea por línea, el rey es bufio.Scanner.
¿Por qué? Porque:
\n- \n
- Es eficiente en memoria — no carga todo el archivo en la RAM \n
- Es fácil de usar — mínimo código \n
- Puede manejar errores — verifica cada paso \n
Pero hay un detalle: el búfer estándar de Scanner es de 64 KB. Si tu línea es más larga, obtendrás un error. Para esos casos se necesitan otros enfoques.
\n\nEjemplo básico: bufio.Scanner
\n\nEmpecemos con la forma más común. Aquí tienes el código mínimo funcional:
\n\npackage main\n\nimport (\n "bufio"\n "fmt"\n "log"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("data.txt")\n if err != nil {\n log.Fatal(err)\n }\n defer file.Close()\n\n scanner := bufio.NewScanner(file)\n for scanner.Scan() {\n line := scanner.Text()\n fmt.Println(line)\n }\n\n if err := scanner.Err(); err != nil {\n log.Fatal(err)\n }\n}\n\n\n¿Qué es importante aquí?
\n- \n
defer file.Close()— cerramos el archivo incluso si hay pánico \n scanner.Text()— devuelve la línea sin el carácter de nueva línea (\\n) \n scanner.Err()— verificamos si hubo errores durante la lectura \n
Este código es ideal para el 90% de las tareas: configuraciones, registros, CSV pequeños.
\n\nEl problema de las líneas largas: aumentar el búfer
\n\nImagina: estás leyendo un archivo con líneas JSON de 100 KB. Scanner fallará con el error bufio.Scanner: token too long. La solución es aumentar el búfer:
scanner := bufio.NewScanner(file)\nbuf := make([]byte, 0, 1024*1024) // 1 MB\nscanner.Buffer(buf, 10*1024*1024) // máximo 10 MB por línea\n\nfor scanner.Scan() {\n // ... procesamiento\n}\n\n\nImportante: el segundo parámetro es el tamaño máximo de línea. Si la línea es más grande, vuelve a aparecer el error. Elige con margen, pero no exageres: la memoria no es infinita.
\n\nCuando Scanner no es adecuado: alternativas
\n\nA pesar de su popularidad, bufio.Scanner no es universal. Aquí hay dos casos en los que deberías considerar otro enfoque:
1. Archivos enormes (gigabytes)
\nSi el archivo es más grande que la memoria disponible (raro, pero pasa), Scanner sigue siendo bueno. Pero si necesitas leer datos binarios o procesar líneas con separadores personalizados, es mejor usar bufio.Reader:
reader := bufio.NewReader(file)\nfor {\n line, err := reader.ReadString('\\n')\n if err != nil {\n break\n }\n // line contiene \\n al final\n line = strings.TrimRight(line, "\\n")\n fmt.Println(line)\n}\n\n\nVentaja: controlas cada operación de lectura. Desventaja: más código y mayor riesgo de errores.
\n\n2. Procesamiento sin cargar en memoria
\nA veces solo necesitas contar líneas o encontrar un patrón. Para esto, bufio.Scanner es adecuado.