W3docs

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 un String formateado.
  • "...".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 un PrintStream (o cualquier Formatter).

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ónArgumentoSignificado
%scualquieratoString() del valor
%denteroentero decimal
%fpunto flotantedecimal de punto fijo
%epunto flotantenotación científica
%gpunto flotanteel más corto entre %e y %f
%x, %oenterohexadecimal / octal
%ccaráctercarácter único
%bcualquieratrue/false (null → "false")
%nningunoseparador de línea apropiado para la plataforma
%%ningunoun % literal
%t...fecha/horatoda 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 locale

Locale.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) lanza IllegalFormatConversionException en tiempo de ejecución — %d requiere entero, %f requiere 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.
  • %s con null. Produce "null". Bien para logs, vergonzoso en salida orientada al usuario.
  • Usar + para números formateados. "Price: " + 19.95 da "Price: 19.95", pero "Price: " + 0.1 + 0.2 da "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.

java— editable, runs on the server

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.

Práctica

Práctica
Quieres formatear un `double` total como string con separador de miles y exactamente dos decimales, y necesitas que el resultado use un punto como separador decimal independientemente de la configuración regional de la JVM (para que un consumidor JSON pueda analizarlo). ¿Qué llamada hace eso correctamente?
Quieres formatear un `double` total como string con separador de miles y exactamente dos decimales, y necesitas que el resultado use un punto como separador decimal independientemente de la configuración regional de la JVM (para que un consumidor JSON pueda analizarlo). ¿Qué llamada hace eso correctamente?
Was this page helpful?