Leer un archivo de texto en C++: línea a línea, palabras, números y el archivo entero

La forma correcta de leer un archivo de texto en C++ es while (std::getline(f, linea)). Aquí la tienes, junto con lo que suele salir mal: el bucle while (!f.eof()) que repite el último dato, los números que se dejan de leer sin avisar y el \r de los archivos creados en Windows.

Todos los programas de esta página leen archivos que están en la misma carpeta desde la que se ejecutan. El primero, notas.txt, tiene tres líneas:

cat notas.txt
salidaPrimera línea
Segunda línea
Tercera línea

La forma correcta: while (std::getline(f, linea))

#include <fstream>
#include <print>
#include <string>

int main()
{
    std::ifstream f("notas.txt");
    if (!f) {
        std::println(stderr, "no se pudo abrir notas.txt");
        return 1;
    }

    std::string linea;
    int n = 0;
    while (std::getline(f, linea)) {
        std::println("{}: {}", ++n, linea);
    }
}
salida1: Primera línea
2: Segunda línea
3: Tercera línea

std::getline lee hasta el salto de línea, lo quita y devuelve el propio flujo, que se puede usar como condición: es cierto si la lectura salió bien. La lectura y la comprobación son la misma operación, así que el bucle se detiene justo cuando ya no queda nada. Y std::string crece lo que haga falta: no hay límite de longitud de línea ni riesgo de salirse de un búfer.

Fíjate también en el if (!f): si el archivo no existe o no hay permiso para leerlo, el ifstream no lanza ninguna excepción, solo queda en estado de error. Sin esa comprobación, el programa simplemente no imprimiría nada.

El bucle que no hay que escribir: while (!f.eof())

Este bucle aparece en miles de tutoriales, y la versión anterior de esta página lo usaba. Tiene dos errores a la vez:

#include <fstream>
#include <print>
#include <string>

int main()
{
    std::ifstream f("notas.txt");
    std::string palabra;
    while (!f.eof()) {
        f >> palabra;
        std::println("[{}]", palabra);
    }
}
salida (incorrecta)[Primera]
[línea]
[Segunda]
[línea]
[Tercera]
[línea]
[línea]
  • Lee palabras, no líneas. El operador >> se detiene en cada espacio o salto de línea.
  • La última palabra sale dos veces. eof() solo se vuelve cierto después de un intento de lectura que choca con el final. Tras leer «línea» todavía queda el último salto de línea, así que hay una vuelta de más: la lectura falla, palabra conserva lo que tenía y se imprime otra vez.

La versión original además leía en un char cadena[128]: una palabra de más de 127 caracteres se habría salido del array. Con std::string ese peligro desaparece. La regla es sencilla: comprueba la lectura, no el final del archivo.

Palabra a palabra y número a número

Si lo que quieres son palabras o números, usa >> como condición del bucle, igual que getline. Con números hay que distinguir entre «se acabó el archivo» y «había algo que no era un número». El archivo numeros.txt tiene una trampa en la segunda línea:

salida10 20 30
40 cuarenta 50
#include <fstream>
#include <print>

int main()
{
    std::ifstream f("numeros.txt");
    int n, suma = 0, leidos = 0;
    while (f >> n) {
        suma += n;
        ++leidos;
    }
    std::println("leídos {} números, suma {}", leidos, suma);

    if (f.eof()) {
        std::println("se llegó al final del archivo");
    } else if (f.fail()) {
        f.clear();                       // quitar el estado de error para poder seguir leyendo
        std::string intruso;
        f >> intruso;
        std::println("lectura detenida: «{}» no es un número", intruso);
    }
}
salidaleídos 4 números, suma 100
lectura detenida: «cuarenta» no es un número

Cuando >> no puede convertir lo que encuentra, activa failbit y deja de leer, aunque queden números detrás. Si no compruebas por qué terminó el bucle, sumas la mitad del archivo sin enterarte. Para datos de verdad suele ser mejor leer línea a línea y convertir cada campo con std::from_chars, que dice exactamente qué falló: lo explicamos en Trabajar con cadenas en C++.

Leer el archivo entero de una vez

#include <algorithm>
#include <fstream>
#include <print>
#include <sstream>
#include <stdexcept>
#include <string>

std::string leer_todo(const char* ruta)
{
    std::ifstream f(ruta, std::ios::binary);
    if (!f) {
        throw std::runtime_error(std::string("no se pudo abrir ") + ruta);
    }
    std::ostringstream contenido;
    contenido << f.rdbuf();
    return contenido.str();
}

int main()
{
    std::string texto = leer_todo("notas.txt");
    std::println("{} bytes", texto.size());
    std::println("{} saltos de línea", std::ranges::count(texto, '\n'));

    try {
        leer_todo("no-existe.txt");
    } catch (const std::exception& e) {
        std::println("error: {}", e.what());
    }
}
salida45 bytes
3 saltos de línea
error: no se pudo abrir no-existe.txt

f.rdbuf() vuelca todo el archivo en el ostringstream de una vez. Son 45 bytes y no 42 caracteres porque la «í» de «línea» ocupa dos bytes en UTF-8: std::string cuenta bytes, no letras. Abrir en modo std::ios::binary evita que, en Windows, los finales de línea \r\n se conviertan al leer, así que obtienes el archivo tal cual está en el disco.

Archivos creados en Windows: el \r al final de cada línea

Los archivos de texto de Windows terminan cada línea con \r\n. En Linux y macOS, getline quita el \n pero deja el \r, invisible pero presente:

#include <fstream>
#include <print>
#include <string>

int main()
{
    std::ifstream f("windows.txt");
    std::string linea;
    while (std::getline(f, linea)) {
        bool trae_r = !linea.empty() && linea.back() == '\r';
        if (trae_r) {
            linea.pop_back();
        }
        std::println("[{}] {} caracteres, traía \\r: {}", linea, linea.size(), trae_r);
    }
}
salida[uno] 3 caracteres, traía \r: true
[dos] 3 caracteres, traía \r: true
[tres] 4 caracteres, traía \r: true

Sin ese pop_back(), comparar linea == "dos" daría falso y el error parecería inexplicable, porque al imprimir la línea el \r no se ve.

Errores típicos al leer archivos

  • ErrorQué pasaSolución
  • while (!f.eof())Una vuelta de más: el último dato sale repetidowhile (std::getline(f, linea)) o while (f >> x)
  • No comprobar que el archivo se abrióEl programa no lee nada y no avisaif (!f) justo después de abrir
  • Ruta relativaSe busca desde el directorio de trabajo, no desde donde está el ejecutableEjecutar desde la carpeta correcta o usar rutas absolutas
  • Líneas de Windows en LinuxQueda un \r al finalQuitarlo con pop_back()
  • Un dato que no es número>> se detiene sin error visibleMirar fail() y eof() al salir, o leer líneas y convertir

La versión anterior de esta página terminaba con system("pause") para que la ventana de la consola de Windows no se cerrase. Ejecuta un programa externo solo para esperar una tecla, y en Linux y macOS esa orden no existe. Si ejecutas el programa desde una terminal abierta, no hace falta.

g++ -std=c++23 -Wall -Wextra leer.cpp -o leer && ./leer

Todo el código de esta página se compiló con GCC 15.3 (C++23, -std=c++23 -Wall -Wextra, sin avisos) y se ejecutó en Linux, en un contenedor limpio sin acceso a la red, antes de publicarla; las salidas están copiadas de esa ejecución.

4 comentarios en «Leer un archivo de texto en C++: línea a línea, palabras, números y el archivo entero»

    • void leerArchivo(){
      string cadena;
      ifstream datos(«Estructura.txt»);
      if(datos.fail()){
      cout<<"Texto entrada Estructura.txt no existe"<<endl;
      }else{
      while(!datos.eof()){
      getline(datos,cadena); //con esta funcion tomas la linea(limitada por \n)
      cout<<cadena<<endl;
      }
      }
      }

      Responder

Deja un comentario