Introducción
Contenido
Hay varias formas de formatear cadenas en Java. Algunos de ellos son de la vieja escuela y se tomaron prestados directamente de viejos clásicos (como printf
de C), mientras que otros están más en el espíritu de la programación orientada a objetos, como la MessageFormat
clase.
En este artículo, pasaremos por alto varios de estos enfoques. Mostraremos algunos detalles de cómo se puede utilizar cada una de las técnicas y en qué circunstancias. Con este conocimiento, sabrá cómo abordar el formato de cadenas y cuál de las técnicas utilizar.
System.out.printf ()
Comencemos con el viejo clásico printf()
. Como se mencionó anteriormente, printf()
proviene del lenguaje de programación C y significa formato de impresión. printf()
Usos bajo el capó, de los java.util.Formatter
que hablaremos más adelante.
La forma en que printf()
funciona se puede explicar por sus argumentos. La forma más común de uso printf()
es la siguiente:
System.out.printf(String format, String... arguments);
Podemos ver que el método espera un format
y un vararg arguments
. El format
argumento define la forma en que desea que se formatee la cadena: una plantilla para el resultado final.
Por ejemplo, es posible que desee imprimir un número decimal con exactamente siete lugares decimales o un número en representación hexadecimal. O puede tener un mensaje predefinido para saludar a los usuarios, pero le gustaría formatearlo para incluir el nombre de usuario.
El arguments
vararg espera convenientemente los argumentos (es decir, valores) para la plantilla String. Por ejemplo, si la plantilla tiene marcadores de posición para dos números, el printf()
método también esperará dos números como arguments
:
System.out.printf("%d %d", 42, 23);
Hemos puesto dos %d
símbolos en la plantilla String. Estos dos símbolos representan marcadores de posición para un cierto tipo de valor. Por ejemplo, %d
es un marcador de posición para un valor numérico decimal. Como tenemos dos de ellos, tenemos que pasar dos argumentos que se correspondan con valores numéricos, como 42
y 23
.
Ejecutar este código producirá:
42 23
Especificadores de formato
Con printf()
, puede imprimir valores como números, cadenas, fechas, etc. Para que el método sepa qué es exactamente lo que está intentando imprimir, debe proporcionar un especificador de formato para cada uno de los valores. Echemos un vistazo a un ejemplo:
System.out.printf("Hello, %s!", "reader");
Si se ejecuta, este código se imprimirá Hello, reader
en la consola. El %s
símbolo representa un especificador de formato para cadenas, similar a cómo %d
representa un especificador de formato para números decimales.
Hay muchos especificadores de formato que podemos utilizar. Éstos son algunos de los más comunes:
- % c – Carácter
- % d – Número decimal (base 10)
- % e – Número de punto flotante exponencial
- % f : número de coma flotante
- % i – Entero (base 10)
- % o – Número octal (base 8)
- % s : cadena
- % u : número decimal sin signo (entero)
- % x – Número hexadecimal (base 16)
- % t – Fecha / hora
- % n : nueva línea
Si queremos imprimir, por ejemplo, un carácter y un número octal, usaríamos especificadores %c
y %o
, respectivamente. Puede notar algo inusual: el especificador de nueva línea. Si no está acostumbrado al printf()
comportamiento de C, puede parecer un poco extraño tener que especificar cosas como esta.
Bueno, printf()
no escribe una nueva línea por defecto. De hecho, no hace casi nada por defecto. Básicamente, si quieres que suceda algo, tienes que hacerlo tú mismo.
Es decir, si tenemos varias printf()
declaraciones sin un especificador de nueva línea:
System.out.printf("Hello, %s!", "Michael Scott");
System.out.printf("Hello, %s!", "Jim");
System.out.printf("Hello, %s!", "Dwight");
El resultado sería:
Hello, Michael Scott!Hello, Jim!Hello, Dwight!
Sin embargo, si incluimos el carácter de nueva línea:
System.out.printf("Hello, %s!%n", "Michael Scott");
System.out.printf("Hello, %s!%n", "Jim");
System.out.printf("Hello, %s!%n", "Dwight");
Entonces el resultado sería:
Hello, Michael Scott!
Hello, Jim!
Hello, Dwight!
Nota: %n
es un formato especial que puede ser uno rn
o solo n
. n
es el símbolo de nueva línea real, mientras que r
es el símbolo de retorno de carro. Por lo general, se recomienda su uso, n
ya que funciona como se espera en todos los sistemas, a diferencia de lo %n
que puede entenderse como cualquiera de los dos. Más sobre esto más adelante.
Personajes de escape
Además de los especificadores de formato descritos anteriormente, hay otro tipo de símbolos de formato: Caracteres de escape.
Imaginemos que queremos imprimir un "
símbolo usando printf()
. Podemos probar algo como:
System.out.printf(""");
Si intenta ejecutar esto, su compilador definitivamente lanzará una excepción. Si observa de cerca, incluso el código que resalta el código en esta página se resaltará );
como una Cadena, y no como el corchete cerrado del método.
Lo que sucedió fue que intentamos imprimir un símbolo que tiene un significado especial y reservado. Las comillas se utilizan para indicar el principio y el final de una cadena.
Comenzamos y terminamos un String ""
, después de lo cual abrimos otro "
pero no lo cerramos. Esto hace que la impresión de caracteres reservados como este sea imposible, utilizando este enfoque.
La forma de evitar esto es escapando . Para imprimir caracteres especiales (como "
) directamente, primero debemos escapar de sus efectos, y en Java eso significa prefijarlos con una barra invertida ( ). Para imprimir legalmente una comilla en Java, haríamos lo siguiente:
System.out.printf(""");
La combinación de y
"
le dice específicamente al compilador que nos gustaría insertar el "
carácter en ese lugar y que debería tratar el "
como un valor concreto, no como un símbolo reservado.
La aplicación del carácter de escape puede invocar diferentes efectos en función del siguiente. Pasar un carácter regular (no reservado) no hará nada y
será tratado como un valor.
Sin embargo, ciertas combinaciones (también llamadas comandos) tienen un significado diferente para el compilador:
Te puede interesar:Diferencia entre ArrayList y LinkedList en Java: código y rendimiento- b – Insertar retroceso
- f : el primer carácter de la siguiente línea comienza a la derecha del último carácter de la línea actual
- n – Insertar nueva línea
- r – Insertar retorno de carro
- t – pestaña Insertar
- \ – Insertar barra invertida
- %% – Insertar signo de porcentaje
Por lo tanto, usaría n
para imprimir un separador de línea en la consola, comenzando efectivamente cualquier contenido nuevo desde el principio de la siguiente línea. De manera similar, para agregar pestañas, usaría el t
especificador.
Es posible que lo hayas notado %%
como la última combinación.
¿Por qué es esto? ¿Por qué no %
se usa simplemente?
El %
carácter ya es un carácter de escape específicamente para el printf()
método. Seguido por personajes como d
, i
, f
, etc, el formateador en tiempo de ejecución sabe cómo tratar a estos valores.
El carácter, sin embargo, es para el compilador. Le dice dónde y qué insertar. El
%
comando simplemente no está definido y usamos el %
carácter de escape para escapar del efecto del %
carácter siguiente , si eso tiene sentido.
Para el compilador, %
no es un carácter especial, pero lo es. Además, es una convención que los caracteres especiales escapen por sí mismos.
escapa
y
%
escapa %
.
Uso básico
Formateemos una cadena con múltiples argumentos de diferentes tipos:
System.out.printf("The quick brown %s jumps %d times over the lazy %s.n", "fox", 2, "dog");
La salida será:
The quick brown fox jumps 2 times over the lazy dog.
Flotación y doble precisión
Con printf()
, podemos definir precisión personalizada para números de punto flotante:
double a = 35.55845;
double b = 40.1245414;
System.out.printf("a = %.2f b = %.4f", a, b);
Dado que %f
se usa para flotantes, podemos usarlo para imprimir double
s. Sin embargo, al agregar un .n
, donde n
es el número de lugares decimales, podemos definir la precisión personalizada.
Ejecutar este código produce:
a = 35.56
b = 40.1245
Formato de relleno
También podemos agregar relleno, incluido el String pasado:
System.out.printf("%10sn", "stack");
Aquí, después del %
carácter, hemos pasado un número y un especificador de formato. Específicamente, queremos una cadena con 10
caracteres, seguida de una nueva línea. Dado que stack
solo contiene 5 caracteres, se agregan 5 más como relleno para «completar» la Cadena en el destino del carácter:
stack
También puede agregar relleno a la derecha en su lugar:
Te puede interesar:Cómo convertir una matriz de Java a ArrayListSystem.out.printf("%-10sn", "stack");
Local
También podemos pasar a Locale
como primer argumento, formateando la Cadena de acuerdo con él:
System.out.printf(Locale.US, "%,dn", 5000);
System.out.printf(Locale.ITALY, "%,dn", 5000);
Esto produciría dos números enteros con formato diferente:
5,000
5.000
Índice de argumentos
Si no se proporciona un índice de argumento, los argumentos simplemente seguirán el orden de presencia en la llamada al método:
System.out.printf("First argument is %d, second argument is %d", 2, 1);
Esto daría como resultado:
First argument is 2, argument number is 1
Sin embargo, después del %
carácter de escape y antes del especificador de formato, podemos agregar otro comando. $n
especificará el índice del argumento:
System.out.printf("First argument is %2$d, second argument is %1$d", 2, 1);
Aquí, 2$
se encuentra entre %
y d
. 2$
especifica que nos gustaría adjuntar el segundo argumento de la lista de argumentos a este especificador. De manera similar, 1$
especifica que nos gustaría adjuntar el primer argumento de la lista al otro especificador.
Ejecutar este código da como resultado:
First argument is 1, second argument is 2
Puede apuntar ambos especificadores al mismo argumento. En nuestro caso, eso significaría que solo usamos un único argumento proporcionado en la lista. Eso está perfectamente bien, aunque todavía tenemos que proporcionar todos los argumentos presentes en la plantilla String:
System.out.printf("First argument is %2$d, second argument is %2$d", 2, 1);
Esto resultará en:
First argument is 1, second argument is 1
System.out.format ()
Antes de hablar System.out.format()
, centrémonos brevemente en System.out
.
Todos los sistemas UNIX tienen tres conductos principales: conducto de entrada estándar ( stdin
), conducto de salida estándar ( stdout
) y conducto de error estándar ( stderr
). El out
campo corresponde a la stdout
tubería y es de PrintStream
tipo.
Esta clase tiene muchos métodos diferentes para imprimir representaciones basadas en texto con formato en una secuencia, algunos de los cuales son format()
y printf()
.
Según la documentación, ambos se comportan exactamente de la misma manera. Esto significa que no hay diferencia entre los dos y se puede utilizar para obtener los mismos resultados. Todo lo que hemos dicho hasta ahora printf()
también funciona format()
.
Ambos printf()
e System.out.format()
imprimen en la stdout
tubería, que normalmente está dirigida a la consola / terminal.
String.format ()
Otra forma de formatear cadenas es con el String.format()
método que también utiliza internamente java.util.Formatter
, que exploraremos en la siguiente sección.
La principal ventaja de String.format()
over printf()
es su tipo de retorno: devuelve un String
. En lugar de simplemente imprimir el contenido en la tubería de salida estándar y no tener un tipo de retorno ( void
) como lo printf()
hace, String.format()
se usa para formatear una Cadena que se puede usar o reutilizar en el futuro:
String formattedString = String.format("Local time: %tT", Calendar.getInstance());
Ahora puede hacer lo que quiera con el formattedString
. Puede imprimirlo, guardarlo en un archivo, modificarlo o conservarlo en una base de datos. Imprimirlo resultaría en:
Local time: 16:01:42
El String.format()
método utiliza exactamente el mismo principio subyacente que el printf()
método. Ambos usan internamente la Formatter
clase para dar formato a las cadenas. Por tanto, todo lo dicho printf()
también se aplica al String.format()
método.
Usar printf()
, String.format()
o Formatter
es esencialmente lo mismo. Lo único que difiere es el tipo de retorno: printf()
imprime en el flujo de salida estándar (normalmente su consola) y String.format()
devuelve un formato String
.
Dicho esto, String.format()
es más versátil, ya que puede utilizar el resultado en más de una forma.
La clase Formatter
Dado que todos los métodos anteriores llaman inherentemente al Formatter
, conocer solo uno significa que los conoce todos.
El uso de Formatter
es bastante similar a otras técnicas mostradas anteriormente. La mayor diferencia es que para usarlo, es necesario crear una instancia de un Formatter
objeto:
Formatter f = new Formatter();
f.format("There are %d planets in the Solar System. Sorry, Pluto", 8);
System.out.println(f);
Esto plantea la pregunta:
¿Por qué no usaría siempre los métodos anteriores, ya que son más concisos?
Hay una distinción más importante que hace que la Formatter
clase sea bastante flexible:
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
formatter.format("%d, %d, %d...n", 1, 2, 3);
En lugar de trabajar solo con String
s, Formatter
también puede trabajar con lo StringBuilder
que hace posible (re) usar ambas clases de manera eficiente.
De hecho, Formatter
es capaz de trabajar con cualquier clase que implemente la Appendable
interfaz. Un ejemplo es el ya mencionado StringBuilder
, pero otros ejemplos incluyen clases, tales como BufferedWriter
, FileWriter
, PrintStream
, PrintWriter
, StringBuffer
, etc. La lista completa se puede encontrar en la documentación .
Por último, todos los especificadores de formato, caracteres de escape, etc., también son válidos para la Formatter
clase ya que es la lógica principal para el formato de cadenas en los tres casos: String.format()
, printf()
, y Formatter
.
MessageFormat
Finalmente, mostremos una técnica de formato final que no se usa Formatter
bajo el capó.
MessageFormat
fue creado para producir y proporcionar mensajes concatenados de una manera neutral en cuanto al lenguaje. Esto significa que el formato será el mismo, independientemente de si está utilizando Java, Python o algún otro lenguaje compatible MessageFormat
.
MessageFormat
extiende la Format
clase abstracta , cómo DateFormat
y cómo NumberFormat
. La Format
clase está destinada a formatear objetos sensibles a la configuración regional en cadenas.
Veamos un buen ejemplo, cortesía de MessageFormat
la documentación de ‘ .
int planet = 7;
String event = "a disturbance in the Force";
String result = MessageFormat.format(
"At {1, time} on {1, date}, there was {2} on planet {0, number, integer}.",
planet, new Date(), event
);
Crédito del código: Oracle Docs
La salida es:
At 11:52 PM on May 4, 2174, there was a disturbance in the Force on planet 7.
En lugar de los especificadores de porcentaje que hemos visto hasta ahora, aquí usamos llaves para cada uno de los argumentos. Tomemos el primer argumento {1, time}
. El número 1
representa el índice del argumento que debe usarse en su lugar. En nuestro caso, los argumentos son planet
, new Date()
y event
.
La segunda parte, se time
refiere al tipo de valor. Tipos de formato de nivel superior son number
, date
, time
, y choice
. Para cada uno de los valores, se puede hacer una selección más específica, como con la {0, number, integer}
que dice que el valor debe tratarse no solo como un número, sino también como un entero.
El conjunto completo de tipos y subtipos de formato se puede encontrar en la documentación .
Conclusión
En este artículo, hemos pasado por alto un buen número de formas de formatear cadenas en el núcleo de Java.
Cada una de las técnicas que hemos mostrado tiene su propia razón de ser. printf()
, por ejemplo, reString al método C de la vieja escuela del mismo nombre de.
Otros enfoques, como Formatter
u MessageFormat
ofrecen un enfoque más moderno que explota algunos de los beneficios de la programación orientada a objetos.
Cada técnica tiene casos de uso específicos, por lo que, con suerte, podrá saber cuándo usar cada una en el futuro.
Te puede interesar:Java: compruebe si el archivo o directorio está vacío