Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Entrada y Salida de Datos

Universidad Nacional de Rio Negro - Sede Andina

Todo programa útil necesita comunicarse con el mundo exterior: mostrar resultados al usuario, recibir datos desde el teclado, leer archivos, o enviar información a otros programas. En Java, esta comunicación se realiza a través de una abstracción denominada streams (flujos de datos).

Si ya se trabajó con C, los conceptos son familiares: printf para imprimir con formato, scanf para leer datos. Java ofrece mecanismos similares pero con diferencias importantes en la sintaxis y el manejo de errores. Este capítulo se centra en la entrada/salida básica por consola; el manejo de archivos se verá más adelante.

¿Qué es un Stream?

Un stream (flujo) es una secuencia ordenada de datos que fluye desde una fuente hacia un destino. La metáfora es la de un río: los datos “fluyen” en una dirección, uno tras otro.

Esta abstracción permite que el mismo código funcione independientemente de si los datos vienen del teclado o de un archivo: el programa solo ve un flujo de datos.

Arquitectura de los Flujos de Sistema

El sistema operativo proporciona tres canales de comunicación estándar que todo programa hereda al iniciarse. En C se accede a ellos mediante stdin, stdout y stderr; en Java, a través de la clase System.

Los Tres Flujos Estándar

La clase System proporciona tres flujos predefinidos:

  1. System.out (salida estándar): El canal principal para mostrar resultados. Es un objeto de tipo PrintStream que ofrece métodos como print(), println() y printf(). Equivale a stdout en C.

  2. System.err (error estándar): El canal para mensajes de error y diagnóstico. También es un PrintStream, pero con una diferencia importante: no tiene búfer, lo que significa que los mensajes aparecen inmediatamente en pantalla. Esto es crucial porque si el programa falla, los mensajes de error ya habrán sido mostrados. Equivale a stderr en C.

  3. System.in (entrada estándar): El canal para recibir datos del usuario (normalmente el teclado). Es un InputStream que procesa bytes crudos. Como trabajar con bytes es incómodo, normalmente se envuelve en clases de más alto nivel como Scanner. Equivale a stdin en C.

¿Qué es un Búfer?

Un búfer (buffer) es una zona de memoria temporal que acumula datos antes de enviarlos a su destino. Imaginá que en lugar de hacer un viaje al supermercado cada vez que necesitás un ingrediente, hacés una lista y vas una sola vez. El búfer funciona igual: acumula varios caracteres y los envía todos juntos, lo cual es más eficiente.

System.out tiene búfer: cuando llamás a print("Hola"), los caracteres pueden no aparecer inmediatamente en pantalla; el sistema espera acumular más datos o que ocurra un salto de línea. System.err no tiene búfer: cada carácter aparece inmediatamente, aunque esto sea menos eficiente.

System.out.print("Mensaje normal... ");  // Puede quedar en el búfer
System.err.print("¡ERROR!");              // Aparece inmediatamente
System.out.println(" continuación");      // El println fuerza la salida

Diferencia entre out y err

Salida de Datos: Métodos de Impresión

Java ofrece tres métodos principales para mostrar información en la consola. Entender cuándo usar cada uno es fundamental para producir salidas claras y bien formateadas.

El método print() imprime el texto y deja el cursor en la misma línea. Es útil cuando se quiere construir una línea de salida en varias partes, o cuando se espera que el usuario ingrese datos en la misma línea del mensaje.

System.out.print("Hola");
System.out.print(" ");
System.out.print("Mundo");
// Resultado: Hola Mundo (todo en la misma línea, cursor al final)

Uso de print()

System.out.print("Ingrese su nombre: ");  // El cursor queda después de ": "
// El usuario escribe en la misma línea

print() para prompts de entrada

En C, esto equivale a printf("texto") sin \n al final.

println() - Impresión Con Salto de Línea

El método println() (del inglés print line) imprime el texto y agrega automáticamente un salto de línea al final. El cursor pasa a la siguiente línea.

System.out.println("Primera línea");
System.out.println("Segunda línea");
// Resultado:
// Primera línea
// Segunda línea

Uso de println()

Este es el método más usado para mostrar mensajes simples. En C, equivale a printf("texto\n") con el \n incluido.

System.out.println("Antes");
System.out.println();  // Línea vacía (solo imprime el salto de línea)
System.out.println("Después");
// Resultado:
// Antes
//
// Después

println() sin argumentos para línea vacía

Concatenación de Valores en print/println

A diferencia de C donde hay que especificar el formato de cada variable, Java permite concatenar valores de cualquier tipo usando el operador +:

int edad = 25;
String nombre = "Ana";
double altura = 1.68;

System.out.println("Nombre: " + nombre);               // Nombre: Ana
System.out.println("Edad: " + edad + " años");         // Edad: 25 años
System.out.println("Altura: " + altura + " metros");   // Altura: 1.68 metros
System.out.println(nombre + " tiene " + edad + " años"); // Ana tiene 25 años

Concatenación con el operador +

Java convierte automáticamente los valores numéricos a texto cuando se concatenan con un String. Esto es muy conveniente pero ofrece poco control sobre el formato (cantidad de decimales, alineación, etc.). Para eso existe printf().

printf() - Impresión Formateada

El método printf() permite controlar exactamente cómo se muestra cada valor mediante especificadores de formato. Es prácticamente idéntico al printf() de C, lo cual facilita la transición.

Importante: printf() no agrega salto de línea automáticamente. Si se quiere terminar la línea, hay que incluir %n o \n en el formato.

System.out.printf(cadenaDeFormato, argumento1, argumento2, ...);

Sintaxis básica de printf()

La cadena de formato contiene texto literal y especificadores que comienzan con %. Cada especificador indica dónde y cómo insertar el valor de un argumento.

int edad = 25;
String nombre = "Ana";
System.out.printf("Nombre: %s, Edad: %d años%n", nombre, edad);
// Resultado: Nombre: Ana, Edad: 25 años

Ejemplo simple de printf()

En este ejemplo:

Comparación con printf() de C

CJavaDescripción
printf("Hola\n");System.out.printf("Hola%n");Impresión simple
printf("%d", x);System.out.printf("%d", x);Entero
printf("%f", x);System.out.printf("%f", x);Flotante
printf("%s", s);System.out.printf("%s", s);Cadena
printf("%.2f", x);System.out.printf("%.2f", x);2 decimales

Como se puede ver, la sintaxis es casi idéntica. La diferencia principal es que en Java se llama como método de System.out y se recomienda usar %n en lugar de \n para portabilidad.

Formateo Completo con printf

El método printf utiliza internamente la clase java.util.Formatter. Es extremadamente potente y permite controlar precisamente el formato de salida. La sintaxis es compatible con la de C, por lo que los conocimientos previos son directamente aplicables.

Sintaxis General de un Especificador de Formato

Cada especificador de formato sigue esta estructura:

%[índice$][banderas][ancho][.precisión]conversión

Parece complicado, pero la mayoría de las partes son opcionales. Desglosemos cada elemento:

Por ejemplo, en %+10.2f:

Especificadores de Conversión

La letra de conversión indica qué tipo de dato se va a formatear. Estos son los especificadores más comunes:

EspecificadorTipo de DatoDescripciónEquivalente en C
%dint, long, byte, shortEntero en base decimal%d
%ffloat, doubleNúmero de punto flotante%f
%efloat, doubleNotación científica (ej: 3.14e+00)%e
%gfloat, doubleUsa %f o %e según convenga%g
%sCualquier tipoCadena de texto (llama a toString())%s
%ccharUn carácter único%c
%bbooleanValor booleano (true o false)
%xint, longEntero en hexadecimal (minúsculas)%x
%Xint, longEntero en hexadecimal (mayúsculas)%X
%oint, longEntero en octal%o
%nSalto de línea (independiente del SO)\n
%%El carácter % literal%%
int entero = 42;
double decimal = 3.14159;
char letra = 'A';
boolean activo = true;

System.out.printf("Entero: %d%n", entero);         // Entero: 42
System.out.printf("Decimal: %f%n", decimal);       // Decimal: 3.141590
System.out.printf("Científico: %e%n", decimal);    // Científico: 3.141590e+00
System.out.printf("Carácter: %c%n", letra);        // Carácter: A
System.out.printf("Booleano: %b%n", activo);       // Booleano: true
System.out.printf("Hexadecimal: %x%n", 255);       // Hexadecimal: ff
System.out.printf("Hexadecimal: %X%n", 255);       // Hexadecimal: FF
System.out.printf("Octal: %o%n", 64);              // Octal: 100
System.out.printf("Porcentaje: 50%% de 100%n");    // Porcentaje: 50% de 100

Ejemplos de especificadores básicos

Ancho de Campo

El ancho especifica el número mínimo de caracteres que ocupará el valor formateado. Si el valor tiene menos caracteres que el ancho especificado, se rellena con espacios.

Por defecto, el relleno se hace a la izquierda (el valor queda alineado a la derecha).

System.out.printf("[%5d]%n", 42);       // [   42]  (5 caracteres, relleno izquierda)
System.out.printf("[%5d]%n", 12345);    // [12345]  (exactamente 5, sin relleno)
System.out.printf("[%5d]%n", 123456);   // [123456] (6 caracteres, el ancho es mínimo)

System.out.printf("[%10s]%n", "Hola");  // [      Hola]  (10 caracteres)
System.out.printf("[%10s]%n", "Mundo"); // [     Mundo]  (10 caracteres)

Uso del ancho de campo

El ancho es especialmente útil para crear columnas alineadas:

System.out.printf("%10s%10s%n", "Nombre", "Edad");
System.out.printf("%10s%10d%n", "Ana", 25);
System.out.printf("%10s%10d%n", "Carlos", 30);
System.out.printf("%10s%10d%n", "María", 28);

// Resultado:
//     Nombre      Edad
//        Ana        25
//     Carlos        30
//      María        28

Columnas alineadas con ancho fijo

Precisión

La precisión tiene diferentes significados según el tipo de dato:

double pi = 3.14159265359;

System.out.printf("Sin precisión: %f%n", pi);   // 3.141593 (6 decimales por defecto)
System.out.printf("2 decimales: %.2f%n", pi);   // 3.14
System.out.printf("4 decimales: %.4f%n", pi);   // 3.1416 (redondeado)
System.out.printf("0 decimales: %.0f%n", pi);   // 3
System.out.printf("8 decimales: %.8f%n", pi);   // 3.14159265

Precisión con números decimales

String texto = "Universidad Nacional";

System.out.printf("Completo: [%s]%n", texto);      // [Universidad Nacional]
System.out.printf("Máximo 5: [%.5s]%n", texto);    // [Unive]
System.out.printf("Máximo 11: [%.11s]%n", texto);  // [Universidad]

Precisión con cadenas

Combinación de Ancho y Precisión

El ancho y la precisión se pueden usar juntos para un control total sobre el formato:

double valor = 3.14159;

// [ancho.precisión]: primero se aplica precisión, luego ancho
System.out.printf("[%10.2f]%n", valor);  // [      3.14]  (10 ancho total, 2 decimales)
System.out.printf("[%8.4f]%n", valor);   // [  3.1416]    (8 ancho total, 4 decimales)
System.out.printf("[%5.2f]%n", valor);   // [ 3.14]       (5 ancho total, 2 decimales)
System.out.printf("[%4.2f]%n", valor);   // [3.14]        (el ancho mínimo se supera)

Ancho y precisión combinados

Para entender el resultado: primero el número se formatea con la precisión indicada (por ejemplo, 3.14 para .2f), y luego se rellena con espacios hasta alcanzar el ancho (10 en el primer ejemplo, resultando 6 espacios + 4 caracteres del número).

Banderas de Formato

Las banderas son caracteres que modifican cómo se presenta el valor. Se colocan inmediatamente después del % y antes del ancho.

BanderaDescripciónEjemploResultado
-Alinear a la izquierda (relleno a la derecha)%-10d con 42[42 ]
+Mostrar siempre el signo (+ para positivos)%+d con 42+42
(espacio)Espacio antes de números positivos% d con 42 42
0Rellenar con ceros en lugar de espacios%05d con 4200042
,Usar separador de miles%,d con 12345671,234,567
(Mostrar negativos entre paréntesis%(d con -42(42)
#Formato alternativo (prefijo 0x para hex)%#x con 2550xff
int positivo = 42;
int negativo = -42;
int grande = 1234567;

// Alineación a la izquierda (por defecto es a la derecha)
System.out.printf("[%-10d]%n", positivo);    // [42        ]
System.out.printf("[%10d]%n", positivo);     // [        42]

// Mostrar signo siempre
System.out.printf("Positivo: [%+d]%n", positivo);   // [+42]
System.out.printf("Negativo: [%+d]%n", negativo);   // [-42]

// Rellenar con ceros
System.out.printf("Con ceros: [%05d]%n", positivo); // [00042]
System.out.printf("Con ceros: [%05d]%n", negativo); // [-0042]

// Separador de miles (muy útil para legibilidad)
System.out.printf("Grande: [%,d]%n", grande);       // [1,234,567]
System.out.printf("Con decimales: [%,.2f]%n", 1234567.89); // [1,234,567.89]

// Negativos entre paréntesis (formato contable)
System.out.printf("Contable: [%(d]%n", negativo);   // [(42)]
System.out.printf("Contable: [%(d]%n", positivo);   // [42]

// Espacio antes de positivos (alinea con negativos sin mostrar +)
System.out.printf("Espacio: [% d]%n", positivo);    // [ 42]
System.out.printf("Espacio: [% d]%n", negativo);    // [-42]

// Formato alternativo para hexadecimal
System.out.printf("Hex normal: [%x]%n", 255);       // [ff]
System.out.printf("Hex con prefijo: [%#x]%n", 255); // [0xff]

Ejemplos de banderas

Combinación de Múltiples Banderas

Las banderas se pueden combinar. El orden no importa:

double precio = 1234.5;
double descuento = -50.0;

// Alineado izquierda + signo + ancho + precisión
System.out.printf("[%-+12.2f]%n", precio);     // [+1234.50    ]
System.out.printf("[%-+12.2f]%n", descuento);  // [-50.00      ]

// Relleno con ceros + ancho + precisión
System.out.printf("[%010.2f]%n", precio);      // [0001234.50]
System.out.printf("[%010.2f]%n", descuento);   // [-000050.00]

// Separador de miles + precisión
System.out.printf("[%,.2f]%n", 1234567.89);    // [1,234,567.89]

// Signo + separador de miles
System.out.printf("[%+,d]%n", 1234567);        // [+1,234,567]

Banderas combinadas

Indexación de Argumentos

Por defecto, los especificadores consumen los argumentos en orden: el primer % usa el primer argumento, el segundo % usa el segundo, etc. Pero podés especificar explícitamente qué argumento usar con la notación n$:

// Sin índices: los argumentos se usan en orden
System.out.printf("%s %s%n", "Hola", "Mundo");  // Hola Mundo

// Con índices: podés reutilizar o reordenar
System.out.printf("%1$s %2$s %1$s%n", "Hola", "Mundo");  // Hola Mundo Hola
System.out.printf("%2$s %1$s%n", "Hola", "Mundo");       // Mundo Hola

Reutilización de argumentos

Esto es especialmente útil para mostrar el mismo valor en diferentes formatos:

int numero = 255;
System.out.printf("Decimal: %1$d, Hex: %1$x, Octal: %1$o%n", numero);
// Resultado: Decimal: 255, Hex: ff, Octal: 377

// También útil para traducciones donde el orden de palabras cambia
String nombre = "Ana";
int edad = 25;
System.out.printf("En español: %1$s tiene %2$d años%n", nombre, edad);
System.out.printf("En inglés: %1$s is %2$d years old%n", nombre, edad);

Mismo valor, múltiples formatos

Tablas Formateadas

Una aplicación práctica de printf es crear tablas con columnas alineadas. La clave está en usar el mismo ancho para cada columna en todas las filas:

// Encabezado: cadenas alineadas a izquierda y números a derecha
System.out.printf("%-15s %10s %10s%n", "Producto", "Cantidad", "Precio");
System.out.printf("%-15s %10s %10s%n", "---------------", "----------", "----------");
System.out.printf("%-15s %10d %10.2f%n", "Manzanas", 50, 1.50);
System.out.printf("%-15s %10d %10.2f%n", "Naranjas", 30, 2.25);
System.out.printf("%-15s %10d %10.2f%n", "Bananas", 100, 0.75);
System.out.printf("%-15s %10s %10.2f%n", "TOTAL", "", 215.00);

// Resultado:
// Producto          Cantidad     Precio
// ---------------   ---------- ----------
// Manzanas                50       1.50
// Naranjas                30       2.25
// Bananas                100       0.75
// TOTAL                          215.00

Ejemplo de tabla formateada

Consejos para tablas:

Formateo de Fechas y Horas

printf tiene soporte para fechas y horas mediante especificadores que comienzan con t (minúscula) o T (mayúscula para versiones en mayúsculas). Requieren un objeto que represente fecha/hora como argumento.

EspecificadorDescripciónEjemplo de Salida
%tHHora en formato 24h (00-23)14
%tIHora en formato 12h (01-12)02
%tMMinutos (00-59)30
%tSSegundos (00-59)45
%tTHora completa 24h (HH:MM:SS)14:30:45
%trHora completa 12h (hh:mm:ss AM/PM)02:30:45 PM
%tdDía del mes (01-31)25
%tmMes (01-12)03
%tYAño (4 dígitos)2026
%tyAño (2 dígitos)26
%tDFecha formato USA (MM/DD/YY)03/25/26
%tFFecha formato ISO (YYYY-MM-DD)2026-03-25
%tANombre del día (completo)Wednesday
%taNombre del día (abreviado)Wed
%tBNombre del mes (completo)March
%tbNombre del mes (abreviado)Mar
// Requiere: import java.time.LocalDateTime;
LocalDateTime ahora = LocalDateTime.now();

System.out.printf("Hora: %tT%n", ahora);           // Hora: 14:30:45
System.out.printf("Fecha ISO: %tF%n", ahora);      // Fecha ISO: 2026-03-25
System.out.printf("Fecha USA: %tD%n", ahora);      // Fecha USA: 03/25/26

// Usando índices para reutilizar el mismo argumento
System.out.printf("Completo: %1$tF %1$tT%n", ahora);  // 2026-03-25 14:30:45
System.out.printf("Día: %1$tA %1$td de %1$tB%n", ahora); // Wednesday 25 de March

Ejemplos de formato de fecha/hora

Entrada de Datos con Scanner

En C, la entrada de datos se hace típicamente con scanf, que lee directamente de stdin parseando según especificadores de formato. Java no tiene un equivalente directo a scanf incorporado en el lenguaje; en su lugar, se usa la clase Scanner.

Scanner es más flexible que scanf: puede leer de múltiples fuentes (teclado, archivos, cadenas), tiene manejo de errores más robusto, y ofrece métodos específicos para cada tipo de dato. Sin embargo, tiene algunas peculiaridades que hay que entender para usarlo correctamente.

Creación de un Scanner

Para leer del teclado, se crea un Scanner que envuelve a System.in:

// Requiere agregar al inicio del archivo:
import java.util.Scanner;

// En el código:
Scanner scanner = new Scanner(System.in);

Creación de un Scanner para leer del teclado

El Scanner actúa como intermediario: lee bytes de System.in, los convierte a texto, y ofrece métodos para extraer valores de diferentes tipos.

Métodos de Lectura

Scanner ofrece un método nextX() para cada tipo de dato primitivo:

MétodoTipo de RetornoLee...Equivalente en C
next()StringSiguiente token (hasta espacio/enter)
nextLine()StringToda la línea (hasta Enter)fgets()
nextInt()intUn número enteroscanf("%d", &x)
nextLong()longUn entero largoscanf("%ld", &x)
nextDouble()doubleUn número decimalscanf("%lf", &x)
nextFloat()floatUn decimal de precisión simplescanf("%f", &x)
nextBoolean()booleantrue o false
nextByte()byteUn byte
nextShort()shortUn entero cortoscanf("%hd", &x)
Scanner scanner = new Scanner(System.in);

System.out.print("Ingrese su nombre: ");
String nombre = scanner.nextLine();  // Lee toda la línea

System.out.print("Ingrese su edad: ");
int edad = scanner.nextInt();        // Lee un entero

System.out.print("Ingrese su altura en metros: ");
double altura = scanner.nextDouble(); // Lee un decimal

System.out.printf("Hola %s, tenés %d años y medís %.2f metros.%n", 
                  nombre, edad, altura);

Ejemplos de lectura con Scanner

¿Qué es un Token?

Un token es una secuencia de caracteres delimitada por espacios en blanco (espacios, tabulaciones, saltos de línea). Scanner trabaja internamente dividiendo la entrada en tokens.

Por ejemplo, si el usuario escribe "Hola Mundo 42" y presiona Enter, hay tres tokens: "Hola", "Mundo", y "42".

Diferencia entre next() y nextLine()

Esta diferencia es crucial y fuente de muchos errores:

Scanner scanner = new Scanner(System.in);

// El usuario escribe: "Juan Pérez" y presiona Enter
System.out.print("Ingrese nombre completo: ");

// Opción 1: usando next()
String palabra1 = scanner.next();     // palabra1 = "Juan"
String palabra2 = scanner.next();     // palabra2 = "Pérez"

// Opción 2: usando nextLine()
String lineaCompleta = scanner.nextLine();  // lineaCompleta = "Juan Pérez"

Diferencia entre next() y nextLine()

// Entrada del usuario: "abc def ghi" (en una línea)
String token1 = scanner.next();       // "abc"
String token2 = scanner.next();       // "def"
String resto = scanner.nextLine();    // " ghi" (nota el espacio al inicio)

next() vs nextLine() con múltiples tokens

Verificación de Entrada con hasNext

Antes de leer, se puede verificar si hay datos disponibles y si son del tipo esperado. Esto permite evitar errores si el usuario ingresa algo inesperado:

MétodoPregunta que responde
hasNext()¿Hay al menos un token disponible?
hasNextLine()¿Hay otra línea disponible?
hasNextInt()¿El siguiente token puede leerse como entero?
hasNextDouble()¿El siguiente token puede leerse como decimal?
hasNextBoolean()¿El siguiente token es true o false?
System.out.print("Ingrese un número entero: ");

if (scanner.hasNextInt()) {
    int numero = scanner.nextInt();
    System.out.println("Leíste el número: " + numero);
} else {
    System.out.println("Error: no ingresaste un número válido");
    String invalido = scanner.next();  // Descartar la entrada inválida
    System.out.println("Ingresaste: " + invalido);
}

Validación de entrada con hasNextInt()

Este patrón es útil para validar entrada antes de procesarla, evitando excepciones por datos mal formados.

El Problema del Salto de Línea Residual

Este es el error más común y confuso al usar Scanner. Ocurre cuando se mezclan métodos como nextInt() o nextDouble() con nextLine().

El problema: Los métodos nextInt(), nextDouble(), etc., leen solo el valor numérico y dejan el carácter de salto de línea (\n) en el búfer. Si después se llama a nextLine(), este encuentra el \n inmediatamente y retorna una cadena vacía.

Scanner scanner = new Scanner(System.in);

System.out.print("Ingrese su edad: ");
int edad = scanner.nextInt();
// El usuario escribe "25" y presiona Enter
// nextInt() lee "25", pero el '\n' queda en el búfer

System.out.print("Ingrese su nombre: ");
String nombre = scanner.nextLine();
// nextLine() encuentra '\n' inmediatamente → retorna ""

System.out.println("Nombre: [" + nombre + "]");  // Imprime: Nombre: []
System.out.println("¡El nombre está vacío!");

El problema del salto de línea residual

Visualización del búfer:

Entrada del usuario: "25\nJuan\n"
                     ↑
Después de nextInt():  Búfer contiene "\nJuan\n"
                       ↑ cursor aquí
Después de nextLine(): Búfer contiene "Juan\n"
                       ↑ cursor aquí (leyó "" antes del primer \n)
Scanner scanner = new Scanner(System.in);

System.out.print("Ingrese su edad: ");
int edad = scanner.nextInt();
scanner.nextLine();  // ← IMPORTANTE: Limpia el '\n' residual

System.out.print("Ingrese su nombre: ");
String nombre = scanner.nextLine();  // Ahora lee correctamente

System.out.println("Hola " + nombre + ", tenés " + edad + " años.");

Solución al problema del salto de línea

Este problema no existe en C con scanf porque scanf("%d", &x) también deja el \n, pero scanf("%s", s) lo ignora automáticamente. Sin embargo, si en C usás fgets() después de scanf(), tendrás el mismo problema.

Patrón Alternativo: Leer Todo como Línea

Una forma de evitar completamente el problema del salto de línea residual es siempre usar nextLine() y convertir manualmente cuando se necesite un número:

Scanner scanner = new Scanner(System.in);

System.out.print("Ingrese su edad: ");
String lineaEdad = scanner.nextLine();
int edad = Integer.parseInt(lineaEdad);  // Convierte String a int

System.out.print("Ingrese su nombre: ");
String nombre = scanner.nextLine();  // No hay problema de búfer

System.out.print("Ingrese su altura: ");
String lineaAltura = scanner.nextLine();
double altura = Double.parseDouble(lineaAltura);  // Convierte String a double

System.out.printf("%s tiene %d años y mide %.2f metros.%n", nombre, edad, altura);

Leer todo como línea y convertir

Métodos de conversión disponibles:

Localización y Separador Decimal

Scanner es sensible a la configuración regional (Locale) del sistema. En países hispanohablantes, el separador decimal suele ser la coma (,), mientras que en el estándar de programación y en países anglosajones se usa el punto (.).

Si el sistema está configurado en español y el usuario escribe 3.14, nextDouble() puede fallar o interpretar incorrectamente el valor. Para forzar el uso del punto como separador decimal (lo recomendado en programación):

import java.util.Scanner;
import java.util.Locale;

Scanner scanner = new Scanner(System.in);
scanner.useLocale(Locale.US);  // Fuerza el punto como separador decimal

System.out.print("Ingrese un decimal (con punto): ");
double valor = scanner.nextDouble();  // Ahora acepta 3.14
System.out.printf("Leíste: %.4f%n", valor);

Configurar Scanner para usar punto decimal

En C, scanf también es sensible al locale, pero es menos común encontrar el problema porque la configuración por defecto suele usar punto.

Cierre del Scanner

Scanner utiliza recursos del sistema (conexión al stream de entrada). Es buena práctica cerrarlo cuando ya no se necesita:

Scanner scanner = new Scanner(System.in);

// ... usar el scanner para leer datos ...

scanner.close();  // Libera recursos

Cerrar el Scanner

Lectura de Datos en un Lazo

Un patrón común es leer datos repetidamente hasta que el usuario indique que quiere terminar. Hay varias formas de hacerlo:

Scanner scanner = new Scanner(System.in);
scanner.useLocale(Locale.US);

int suma = 0;
int contador = 0;

System.out.println("Ingrese números (0 para terminar):");

int numero = scanner.nextInt();
while (numero != 0) {
    suma = suma + numero;
    contador = contador + 1;
    numero = scanner.nextInt();
}

if (contador > 0) {
    double promedio = (double) suma / contador;
    System.out.printf("Promedio de %d números: %.2f%n", contador, promedio);
} else {
    System.out.println("No se ingresaron números.");
}

Lectura en lazo con valor centinela

Otra opción es usar hasNext() para detectar cuándo no hay más entrada (útil cuando los datos vienen de un archivo):

Scanner scanner = new Scanner(System.in);

// hasNextInt() retorna false cuando no hay más entrada
while (scanner.hasNextInt()) {
    int numero = scanner.nextInt();
    System.out.println("Leíste: " + numero);
}

System.out.println("Fin de la entrada");

Lectura hasta fin de entrada

Este segundo patrón es especialmente útil cuando la entrada viene de un archivo mediante redirección en la línea de comandos:

java MiPrograma < datos.txt

En C, el equivalente sería while (scanf("%d", &x) == 1) para detectar el fin de archivo.

Comparación de Métodos de Salida

Esta tabla resume cuándo usar cada método de impresión:

MétodoSalto de LíneaPermite FormatoUso Típico
print()NoNoMensajes parciales, prompts
println()Sí (automático)NoMensajes simples, depuración
printf()No (usar %n)Tablas, reportes, formato preciso
double precio = 19.99;
String producto = "Manzanas";

// print(): para construir líneas en partes
System.out.print("Producto: ");
System.out.print(producto);
System.out.print(" - ");
System.out.println(precio);  // println al final para el salto

// println(): para mensajes simples
System.out.println("Precio: " + precio);
System.out.println(producto + " cuesta $" + precio);

// printf(): para control preciso del formato
System.out.printf("%-15s $%8.2f%n", producto, precio);

Comparación práctica de los tres métodos

Recomendación general:

Secuencias de Escape

Las secuencias de escape permiten incluir caracteres especiales dentro de las cadenas de texto. Comienzan con una barra invertida (\) seguida de un carácter que indica qué carácter especial representar.

SecuenciaSignificadoEjemplo
\nSalto de línea (new line)"Línea 1\nLínea 2"
\tTabulación horizontal"Col1\tCol2"
\\Barra invertida literal"C:\\Users"
\"Comilla doble literal"Él dijo \"Hola\""
\'Comilla simple literal'It\'s' (en char)
\rRetorno de carroUsado en Windows
\bRetroceso (backspace)Borra un carácter
// Salto de línea dentro de una cadena
System.out.println("Primera línea\nSegunda línea");
// Resultado:
// Primera línea
// Segunda línea

// Tabulaciones para alinear
System.out.println("Nombre\tEdad\tCiudad");
System.out.println("Ana\t25\tNeuquén");
System.out.println("Carlos\t30\tRoca");

// Barras invertidas (necesarias para rutas en Windows)
System.out.println("Ruta: C:\\Users\\Juan\\Documentos");
// Resultado: Ruta: C:\Users\Juan\Documentos

// Comillas dentro de cadenas
System.out.println("Él dijo: \"Hola, ¿cómo estás?\"");
// Resultado: Él dijo: "Hola, ¿cómo estás?"

Uso de secuencias de escape

Estas secuencias son idénticas a las de C. La razón por la que \\ representa una sola barra es que la barra invertida es el carácter de escape, así que para incluirla literalmente hay que “escaparla” con otra barra.

Temas Avanzados (Referencia)

Esta sección menciona brevemente temas que se profundizarán más adelante o que son útiles como referencia.

System.console() - Interacción con Terminal

Para aplicaciones que se ejecutan en una terminal real (no en un IDE), Java ofrece System.console():

Console console = System.console();
if (console != null) {
    String nombre = console.readLine("Ingrese su nombre: ");
    char[] password = console.readPassword("Ingrese su contraseña: ");
    // password no se muestra mientras se escribe
}

Uso básico de Console

Ventajas:

Desventaja:

Rendimiento: Scanner vs. BufferedReader

Para la mayoría de los programas, Scanner es suficiente. Sin embargo, tiene un costo de rendimiento:

En concursos de programación o procesamiento de archivos grandes (millones de líneas), Scanner puede ser el cuello de botella. Para la mayoría de las tareas académicas y profesionales, la diferencia es imperceptible.

Ejercicios

Los siguientes ejercicios permiten practicar los conceptos de entrada y salida. Se recomienda resolverlos antes de ver las soluciones.

Solution to Exercise 1

El problema es que nextInt() lee 42 pero deja el \n (salto de línea) en el búfer. Cuando se ejecuta nextLine(), encuentra inmediatamente el \n, asume que la línea terminó, y devuelve una cadena vacía.

Salida del programa con el bug:

Nombre: []
Edad: 42

Solución: Agregar una llamada a nextLine() después de nextInt() para limpiar el salto de línea residual:

Scanner scanner = new Scanner(System.in);
int edad = scanner.nextInt();
scanner.nextLine();  // ← Limpia el \n residual
String nombre = scanner.nextLine();
System.out.println("Nombre: [" + nombre + "]");
System.out.println("Edad: " + edad);

Salida corregida:

Nombre: [Juan]
Edad: 42
Solution to Exercise 2
double numero = 3.14159;
System.out.printf("[%+10.3f]%n", numero);

Resultado: [ +3.142]

Explicación de los especificadores:

  • + → muestra siempre el signo (+ para positivos, - para negativos)

  • 10 → ancho mínimo de 10 caracteres

  • .3 → precisión de 3 decimales (el valor se redondea)

  • f → formato de punto flotante

El número formateado es +3.142 (6 caracteres), y como el ancho es 10, se agregan 4 espacios a la izquierda.

Solution to Exercise 3
System.out.printf("%-10s %12s%n", "Celsius", "Fahrenheit");
System.out.printf("%-10s %12s%n", "----------", "------------");

int[] celsius = {0, 10, 20, 30, 40};
for (int c : celsius) {
    double f = c * 9.0 / 5.0 + 32;
    System.out.printf("%-10d %12.1f%n", c, f);
}

Resultado:

Celsius    Fahrenheit
---------- ------------
0                 32.0
10                50.0
20                68.0
30                86.0
40               104.0

Explicación:

  • %-10s y %-10d: alinean a la izquierda en 10 caracteres

  • %12.1f: alinea a la derecha en 12 caracteres con 1 decimal

Solution to Exercise 4
import java.util.Scanner;

public class LecturaValidada {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int numero = -1;
        
        while (numero < 0) {
            System.out.print("Ingrese un número entero positivo: ");
            
            if (scanner.hasNextInt()) {
                numero = scanner.nextInt();
                if (numero < 0) {
                    System.out.println("Error: el número debe ser positivo.");
                }
            } else {
                System.out.println("Error: debe ingresar un número entero.");
                scanner.next();  // Descartar la entrada inválida
            }
        }
        
        System.out.println("Número válido: " + numero);
    }
}

Ejemplo de ejecución:

Ingrese un número entero positivo: hola
Error: debe ingresar un número entero.
Ingrese un número entero positivo: -5
Error: el número debe ser positivo.
Ingrese un número entero positivo: 42
Número válido: 42

La clave es usar hasNextInt() para verificar si lo que sigue es un entero, y scanner.next() para descartar la entrada inválida cuando no lo es.

Referencias Bibliográficas


- {ref}`regla-0x0001` - Convenciones de nomenclatura.
- {ref}`regla-0x3001` - Manejo de errores en la entrada de datos.