Formateo de Strings en Java
Formatea strings en Java con String.format y System.out.printf usando especificadores como %s, %d, %f y %n.
La concatenación (+) está bien para cadenas cortas y simples. En el momento en que quieres fijar un número a un número específico de decimales, alinear columnas con relleno, incrustar una fecha con un formato consistente, o en general formatear valores en lugar de simplemente pegarlos, recurrirás a la API estilo printf que Java tomó prestada de C.
Hay tres puntos de entrada estrechamente relacionados que verás en código real:
String.format(fmt, args...)— devuelve unStringformateado."...".formatted(args...)— forma de instancia añadida en Java 15. Resultado idéntico, más amigable para encadenamiento.System.out.printf(fmt, args...)— imprime directamente en unPrintStream(o cualquierFormatter).
Los tres comparten la misma sintaxis de especificadores de formato. Apréndelaa una vez.
Especificadores de formato
Un especificador tiene la forma %[flags][width][.precision]conversion. La letra de conversión es la única pieza obligatoria. El resto ajusta el ancho, la alineación, el relleno y la precisión.
String s = String.format("%-10s | %5d | %8.2f", "apples", 42, 3.14159);
// "apples | 42 | 3.14"Las conversiones más útiles:
| Conversión | Argumento | Significado |
|---|---|---|
%s | cualquiera | toString() del valor |
%d | entero | entero decimal |
%f | punto flotante | decimal de punto fijo |
%e | punto flotante | notación científica |
%g | punto flotante | el más corto entre %e y %f |
%x, %o | entero | hexadecimal / octal |
%c | carácter | carácter único |
%b | cualquiera | true/false (null → "false") |
%n | ninguno | separador de línea apropiado para la plataforma |
%% | ninguno | un % literal |
%t... | fecha/hora | toda una familia — %tF, %tT, etc. |
El ancho rellena la salida con al menos N caracteres; la precisión significa "dígitos decimales" para flotantes y "máximo de caracteres" para strings.
Flags: alineación, signo, relleno con ceros, agrupación
Hay una pequeña cantidad de flags que van entre % y el ancho:
-— alinear a la izquierda dentro del ancho. Por defecto es alineación a la derecha.0— rellenar con ceros hasta el ancho (solo para conversiones numéricas).+— mostrar siempre el signo en los números (+42,-7).(espacio) — mostrar un espacio inicial para números positivos, como+pero con un espacio en blanco.,— agrupar dígitos usando el separador de miles de la configuración regional.(— envolver los números negativos entre paréntesis, estilo contable.
String.format("%08d", 42); // "00000042"
String.format("%,d", 1234567); // "1,234,567"
String.format("%+.2f", 3.14159); // "+3.14"
String.format("%-10s|", "hi"); // "hi |"Ancho y precisión
El ancho es el ancho mínimo del campo — si el valor formateado es más amplio, nada se trunca.
La precisión significa cosas diferentes para distintas conversiones:
%.3f— tres dígitos después del punto decimal.%.10s— truncar el string a un máximo de 10 caracteres.%.4e— cuatro dígitos de precisión de mantisa.
Combinar ancho y precisión es común cuando quieres que las columnas se alineen y que los números estén redondeados:
String.format("%10.4f", Math.PI); // " 3.1416"
String.format("%-10.4s", "abcdef"); // "abcd "%n vs \n
%n emite el separador de línea apropiado para la plataforma: "\n" en Unix, "\r\n" en Windows. \n es siempre exactamente un byte. Para archivos y salida de protocolo donde el final de línea importa con precisión, prefiere \n y elige conscientemente. Para la salida en consola que debe verse bien en cualquier SO en el que esté corriendo la JVM, %n es la opción más segura.
Índices de argumentos: reutilización y reordenamiento
Un especificador de la forma %N$... hace referencia al argumento N-ésimo (base 1). Es útil cuando un valor aparece más de una vez en una plantilla, o cuando el orden de lectura natural difiere del orden de los argumentos:
String.format("%1$s, %1$s, %1$s!", "go"); // "go, go, go!"
String.format("%2$s before %1$s", "lunch", "tea"); // "tea before lunch"Esta es la herramienta correcta al localizar plantillas — diferentes idiomas colocan los sustantivos en posiciones distintas, y un traductor puede reorganizar los marcadores de posición sin tocar el código de la llamada.
La configuración regional importa para números y fechas
El formateo de números respeta la configuración regional predeterminada de la JVM a menos que se anule. En en-US obtienes 3.14; en de-DE obtienes 3,14; en fr-FR los miles se agrupan con un espacio de no separación. Para la salida orientada al usuario, eso es generalmente lo que quieres. Para formatos de datos — JSON, CSV, archivos de log, cualquier cosa legible por máquina — es un desastre esperando un despliegue en Frankfurt.
Pasa siempre una configuración regional explícita para la salida legible por máquina:
String json = String.format(Locale.ROOT, "{\"price\": %.2f}", 19.95);
// "{\"price\": 19.95}" — always, regardless of JVM localeLocale.ROOT significa "sin formato específico de configuración regional" — punto como decimal, sin agrupación. Locale.US es la otra opción común para el mismo propósito. Lo peligroso es no pasar una configuración regional y asumir.
Formateo de fecha/hora
%t es una meta-conversión: la letra que le sigue elige el campo. El mismo argumento Date, Calendar, Long (milisegundos) o java.time.temporal.TemporalAccessor puede formatearse de muchas maneras:
LocalDateTime now = LocalDateTime.of(2026, 5, 29, 14, 30, 15);
String.format("%tF", now); // "2026-05-29" — ISO date
String.format("%tT", now); // "14:30:15" — 24-hour time
String.format("%tA", now); // "Friday" — locale-dependent
String.format("%1$tF %1$tT", now); // "2026-05-29 14:30:15"Para cualquier cosa más allá del formateo rápido, la API java.time.format.DateTimeFormatter es más flexible y consciente de la configuración regional — pero %tF y similares siguen siendo prácticos en líneas de log.
Errores comunes
- Conversión incorrecta para el tipo.
String.format("%d", 3.14)lanzaIllegalFormatConversionExceptionen tiempo de ejecución —%drequiere entero,%frequiere punto flotante. El compilador no puede comprobarlo. - Argumentos faltantes. Olvidar un argumento de marcador de posición lanza
MissingFormatArgumentException. - Decimal dependiente de configuración regional en salida de máquina. Cubierto anteriormente.
%sconnull. Produce"null". Bien para logs, vergonzoso en salida orientada al usuario.- Usar
+para números formateados."Price: " + 19.95da"Price: 19.95", pero"Price: " + 0.1 + 0.2da"Price: 0.10.2", no"Price: 0.3"— concatenación, no adición.
Un ejemplo práctico
Un pequeño formateador de resumen de pedidos que ejercita ancho, precisión, alineación, agrupación, configuración regional y la conversión de fecha %t. La salida es un informe ordenado de dos columnas — el tipo de cosa que de otro modo construirías a mano con ayudantes padLeft.
Dos cosas a destacar: el %<tT en la línea "Placed" reutiliza el argumento anterior (< es el flag de referencia hacia atrás), evitando un segundo placedAt redundante. Y la línea JSON usa Locale.ROOT — el mismo código en una JVM alemana sigue emitiendo 1339.43, no 1339,43, que es exactamente lo que espera un analizador JSON.
Qué sigue
Construir strings es la mitad del trabajo. Compararlos — igualdad, ordenación, plegado de mayúsculas/minúsculas, la diferencia entre == y equals — es la otra mitad y una fuente frecuente de errores sutiles. Continúa en Comparación de strings en Java.