CodeGym /Cursos /C++ SELF /Lectura de líneas — std::getline

Lectura de líneas — std::getline

C++ SELF
Nivel 2, Lección 4
Disponible

1. Conociendo std::getline

Si acabas de aprender std::cin >>, puede parecer que hace de todo: introduces «una palabra», introduces un número, todo funciona. Pero hasta que el usuario introduce una frase, el nombre completo, el título de un libro o la dirección —es decir, texto con espacios—. Y entonces >> se comporta como un robot demasiado literal: ve un espacio y considera que «bueno, la palabra terminó, me voy».

Imagina que std::cin >> es un torniquete del metro: deja pasar exactamente a un pasajero (un token) y se cierra ante el primer «obstáculo» como un espacio o '\n'. Y std::getline es la puerta del vagón: entras completo, con todas tus maletas-espacios, y nadie te corta el apellido por la mitad.

Idea clave: std::cin >> lee «una palabra/un número» (un token), y std::getline lee la línea completa hasta el final de la línea.

Cómo funciona std::getline

Cuando quieres leer toda la línea, incluidos los espacios, necesitas std::getline. Es una función de la biblioteca estándar, normalmente se usa con std::string. Importante: la línea se lee hasta el carácter de nueva línea '\n', pero el propio '\n' no forma parte del resultado —es una frontera, no parte del texto.

El patrón mínimo se ve así:

#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);

    std::cout << "Line: " << line << '\n';
}

Hay varios puntos muy importantes aquí.

En primer lugar, std::getline puede leer incluso una línea vacía —no es un error ni «se rompió», es una entrada completamente válida.

En segundo lugar, std::getline no teme a los espacios: si el usuario escribe Ivan Ivanov, la cadena seguirá siendo Ivan Ivanov, no se convertirá en una sola Ivan.

Para ver los límites más claramente (especialmente cuando la línea está vacía), es útil imprimir la cadena entre corchetes:

#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);

    std::cout << "[" << line << "]\n"; // p. ej.: [Hello world]
}

2. Líneas vacías y cómo detectarlas

Una línea vacía es la situación en la que el usuario simplemente pulsa Enter y no escribe ningún carácter. Los principiantes a menudo lo interpretan como «getline no funciona», pero en realidad es una cadena perfectamente válida, simplemente de longitud 0. En programación la ausencia también es dato (a veces incluso el más honesto).

Hagamos un ejemplo que muestre claramente la longitud de la línea. Así distinguirás con seguridad «vacío» de «no leído»:

#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);

    std::cout << "Text=[" << line << "]\n";
    std::cout << "Size=" << line.size() << '\n'; // Size=0 para una línea vacía
}

Ten en cuenta: line.size() devuelve el número de caracteres. Si el usuario escribió Hi, el tamaño será 2. Si solo pulsó Enter, será 0.

Las líneas vacías pueden aparecer en dos escenarios principales.

  • Primero —el usuario realmente introdujo una línea vacía (pulsó Enter).
  • Segundo —más traicionero: has mezclado std::cin >> y std::getline, y getline ha capturado el «resto» del salto de línea.

Es como si fueses a recoger una pizza y te entregaran la caja… pero vacía, porque alguien se comió la pizza en la clase anterior. Ahora analizaremos ese argumento.

3. La trampa después de std::cin >>: el resto del salto de línea

Cuando std::cin >> lee un token, toma caracteres hasta el siguiente espacio/tabulación/salto de línea. Detalle importante: el separador (por ejemplo '\n') permanece en el flujo de entrada. Es decir, conceptualmente, el «cursor de lectura» se detuvo justo antes de '\n'.

Y std::getline trabaja exactamente así: «leo hasta '\n'». Si el siguiente carácter no leído en la entrada es inmediatamente '\n', entonces getline piensa: «Ah, el fin de la línea ya está aquí» y devuelve una cadena vacía.

Veamos un escenario típico que rompe la cabeza:

  • el usuario introduce la edad 25 y pulsa Enter
  • luego introduce el nombre Ivan Ivanov y pulsa Enter

En los datos de entrada se ve más o menos así (el símbolo \n representa Enter):

25\n
Ivan Ivanov\n

Ahora el código:

#include <iostream>
#include <string>

int main() {
    int age = 0;
    std::string name;

    std::cin >> age;
    std::getline(std::cin, name); // ¡trampa!

    std::cout << "Age=" << age << '\n';
    std::cout << "Name=[" << name << "]\n"; // a menudo estará vacío: []
}

Una salida muy probable será así:

Age=25
Name=[]

Y esto no es «C++ malo», sino simplemente la consecuencia de que '\n' después del número quedó en el flujo, y getline lo «consumió» inmediatamente como el final de la línea.

Mini‑esquema: qué ocurre en el flujo de entrada

Imagina la entrada como una cinta y la lectura como un puntero a la posición actual:

flowchart TD
    A["Entrada: 25 luego Ivan Ivanov"] --> B["cin >> age lee 25"]
    B --> C["En la entrada queda el salto de línea y Ivan Ivanov"]
    C --> D["getline ve primero el salto de línea"]
    D --> E["getline devuelve una cadena vacía"]

Esa es la «magia» que en realidad es pura mecánica. Y la buena noticia: también se arregla con un truco mecánico sencillo.

4. Cómo mezclar >> y getline

Cuando por el formato de entrada necesitas primero leer algo con std::cin >> (por ejemplo, un número) y luego una línea completa con std::getline, la forma más sencilla de estabilizar el comportamiento es hacer una llamada adicional a getline en una variable dummy, para consumir el resto de la línea (normalmente ese '\n').

Aquí está la técnica canónica:

#include <iostream>
#include <string>

int main() {
    int age = 0;
    std::string dummy;
    std::string name;

    std::cin >> age;
    std::getline(std::cin, dummy); // consumimos el '\n' restante
    std::getline(std::cin, name);  // ahora leemos la línea real

    std::cout << "Age=" << age << '\n';
    std::cout << "Name=[" << name << "]\n";
}

Ahora, si el usuario introduce:

25
Ivan Ivanov

la salida será la esperada:

Age=25
Name=[Ivan Ivanov]

¿Por qué funciona? Porque el getline transitorio lee lo que quedó después de >> hasta el final de la línea. Normalmente es ese único '\n', pero la lógica también funciona en casos más complejos (por ejemplo, si había espacios después del número).

5. Varias llamadas a getline seguidas

Una vez que pasas a std::getline, leer varias líneas seguidas es muy cómodo: cada línea es una llamada separada. Y aquí ya no existe el problema del «resto de '\n'», porque getline lee hasta '\n' y deja el flujo apuntando a la siguiente línea.

Imagina que queremos leer «ciudad» y «calle» (ambas pueden contener espacios):

#include <iostream>
#include <string>

int main() {
    std::string city;
    std::string street;

    std::getline(std::cin, city);
    std::getline(std::cin, street);

    std::cout << "City=[" << city << "]\n";
    std::cout << "Street=[" << street << "]\n";
}

Si el usuario escribe:

New York
5th Avenue

el programa leerá honestamente ambas líneas completas.

Aquí es importante sentir la diferencia: si lo intentaras con std::cin >>, New York se convertiría en New, y York «viajaría» a la siguiente variable. A veces eso es útil (cuando el formato es estrictamente tokenizado), pero para texto humano a menudo se prefiere getline.

6. Miniaplicación «Formulario»

Vamos a escribir una pequeña aplicación de consola estilo «formulario»: el programa hace preguntas e imprime lo que el usuario ingresó. Hoy la mejoraremos: aprenderá a leer el nombre completo, no solo la primera palabra.

Primero —la versión que parece lógica pero falla (mostramos el error para luego evitarlo):

#include <iostream>
#include <string>

int main() {
    int age = 0;
    std::string fullName;

    std::cout << "Enter age: ";
    std::cin >> age;

    std::cout << "Enter full name: ";
    std::getline(std::cin, fullName); // a menudo vacío

    std::cout << "Age=" << age << '\n';
    std::cout << "Full name=[" << fullName << "]\n";
}

Si la entrada es:

25
Ivan Ivanov

entonces fullName a menudo quedará vacío. Y el usuario te mirará como alguien que «pidió el nombre y luego lo ignoró». Incómodo.

Ahora la versión correcta con getline:

#include <iostream>
#include <string>

int main() {
    int age = 0;
    std::string dummy;
    std::string fullName;

    std::cout << "Enter age: ";
    std::cin >> age;

    std::getline(std::cin, dummy); // consumimos '\n' después del número

    std::cout << "Enter full name: ";
    std::getline(std::cin, fullName);

    std::cout << "Age=" << age << '\n';                 // Age=25
    std::cout << "Full name=[" << fullName << "]\n";    // Full name=[Ivan Ivanov]
}

Esto ya se parece a un programa al que no da vergüenza confiarle la entrada de datos (al menos si el usuario introduce lo que se espera).

Pequeño bonus: la longitud de la línea como «sensor»

A veces es útil, sobre todo con fines didácticos, añadir un «sensor»:

#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);

    std::cout << "Line=[" << line << "]\n";
    std::cout << "Len=" << line.size() << '\n'; // Len=...
}

Así literalmente ves: la línea está vacía o no. En programas reales no se suele hacer, pero para depuración y aprendizaje —ayuda mucho.

7. Errores típicos al trabajar con std::getline

Error nº 1: esperar que std::getline «no pueda» devolver una línea vacía.
Los principiantes suelen pensar: «Si llamé a getline, entonces el usuario escribió algo». Pero el usuario pudo pulsar Enter de inmediato, y la cadena será honestamente vacía. Eso no es un fallo, son datos correctos, y el programa debe estar preparado para que line.size() sea igual a 0.

Error nº 2: mezclar std::cin >> y std::getline sin el paso de transición.
La clásica: leíste un número con >>, luego llamaste inmediatamente a getline —y obtuviste una cadena vacía. La razón es que después de la lectura por token queda en el flujo '\n', que getline interpreta como «fin de línea ahora mismo». Se arregla con una llamada adicional a getline en una variable dummy.

Error nº 3: no mostrar los límites de la línea al depurar y luego discutir con la realidad.
Cuando la línea está vacía, imprimir con std::cout << line; parece como si «no pasó nada». Es muy fácil empezar a sospechar del compilador, del IDE, de las fases de la Luna o del Wi‑Fi del vecino. En realidad basta con imprimir como "[" + line + "]", para ver que la línea está realmente vacía, y eso ya es motivo para investigar la entrada.

Error nº 4: confundir '\n' y "\\n".
'\n' es un único carácter de nueva línea que llega al pulsar Enter (y que getline usa como frontera). Y "\\n" son dos caracteres: barra invertida y letra n, es decir, texto \n. Si los confundes, puedes pasarte mucho tiempo preguntándote por qué «vi \n», pero getline se comporta de otro modo.

Error nº 5: pensar que std::getline es un «mejorado >>» y usarlo para todo sin entender el formato.
getline es realmente útil para texto humano, pero lee «hasta el final de la línea». Si el problema define la entrada en tokens (p. ej., números separados por espacios), getline puede no ser la herramienta adecuada. Buen estilo: primero entiende el formato de entrada y luego elige entre >> y getline.

1
Tarea
C++ SELF, nivel 2, lección 4
Bloqueada
Corchetes del chat
Corchetes del chat
1
Tarea
C++ SELF, nivel 2, lección 4
Bloqueada
Comentario vacío
Comentario vacío
1
Tarea
C++ SELF, nivel 2, lección 4
Bloqueada
Número y frase
Número y frase
1
Tarea
C++ SELF, nivel 2, lección 4
Bloqueada
Dirección a partir de cadenas
Dirección a partir de cadenas
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION