Codificar una cadena a UTF-8 en Java

En este tutorial, veremos cómo codificar una cadena en UTF-8 en Java, usando StandardCharsets, getBytes() con ByteBuffer y Apache Commons con ejemplos.

Introducción

Cuando trabajamos con Strings en Java, a menudo necesitamos codificarlos en un juego de caracteres específico, como UTF-8.

UTF-8 representa una codificación de caracteres de ancho variable que utiliza entre uno y cuatro bytes de ocho bits para representar todos los puntos de código Unicode válidos.

Un punto de código puede representar caracteres individuales, pero también puede tener otros significados, como el formato. "Ancho variable" significa que codifica cada punto de código con un número diferente de bytes (entre uno y cuatro) y, como medida de ahorro de espacio, los puntos de código de uso común se representan con menos bytes que los que se usan con menos frecuencia. .

UTF-8 usa un byte para representar puntos de código de 0-127, lo que hace que los primeros puntos de código 128 sean un mapa uno a uno con caracteres ASCII, por lo que UTF-8 es compatible con versiones anteriores de ASCII.

Nota: Java codifica todas las cadenas en UTF-16, que utiliza un mínimo de dos bytes para almacenar puntos de código. ¿Por qué tendríamos que convertir a UTF-8 entonces?

No todas las entradas pueden ser UTF-16, o UTF-8 para el caso. Es posible que reciba una cadena codificada en ASCII, que no admite tantos caracteres como UTF-8. Además, no todas las salidas pueden manejar UTF-16, por lo que tiene sentido convertirlo a un UTF-8 más universal.

Trabajaremos con algunas Strings que contienen caracteres Unicode que quizás no encuentre a diario, como č, ß y , simulando la entrada del usuario.

Escribamos un par de cadenas:

1
2
3
String serbianString = "Šta radiš?"; // What are you doing?
String germanString = "Wie heißen Sie?"; // What's your name?
String japaneseString = "よろしくお願いします"; // Pleased to meet you.

Ahora, aprovechemos el constructor String(byte[] bytes, Charset charset) de un String, para recrear estos Strings, pero con un Charset diferente, simulando la entrada ASCII que nos llegó en primer lugar:

1
2
3
4
5
6
7
String asciiSerbianString = new String(serbianString.getBytes(), StandardCharsets.US_ASCII);
String asciigermanString = new String(germanString.getBytes(), StandardCharsets.US_ASCII);
String asciijapaneseString = new String(japaneseString.getBytes(), StandardCharsets.US_ASCII);

System.out.println(asciiSerbianString);
System.out.println(asciigermanString);
System.out.println(asciijapaneseString);

Una vez que hayamos creado estas cadenas y las hayamos codificado como caracteres ASCII, podemos imprimirlas:

1
2
ta radi?
Wie heien Sie?

Mientras que las dos primeras cadenas contienen solo unos pocos caracteres que no son caracteres ASCII válidos, la última no contiene ninguno.

Para evitar este problema, podemos suponer que es posible que no todas las entradas ya estén codificadas a nuestro gusto, y codificarlas para resolver esos casos nosotros mismos. Hay varias formas de codificar una cadena en UTF-8 en Java.

Codificar una cadena en Java simplemente significa inyectar ciertos bytes en la matriz de bytes que constituye una cadena, proporcionando información adicional que se puede usar para formatearla una vez que formamos una instancia de String.

Uso del método getBytes()

La clase String, al estar compuesta de bytes, naturalmente ofrece un método getBytes(), que devuelve la matriz de bytes utilizada para crear la cadena. Dado que la codificación es realmente solo manipular esta matriz de bytes, podemos poner esta matriz a través de un Charset para formarlo mientras obtenemos los datos.

De forma predeterminada, sin proporcionar un ‘Charset’, los bytes se codifican utilizando el ‘Charset’ predeterminado de la plataforma, que podría no ser UTF-8 o UTF-16. Obtengamos los bytes de una cadena e imprimámoslos:

1
2
3
4
5
6
String serbianString = "Šta radiš?"; // What are you doing?
byte[] bytes = serbianString.getBytes(StandardCharsets.UTF_8);

for (byte b : bytes) {
    System.out.print(String.format("%s ", b));
}

Esto da como resultado:

1
-59 -96 116 97 32 114 97 100 105 -59 -95 63 

Estos son los puntos de código para nuestros caracteres codificados, y no son realmente útiles para los ojos humanos. Aunque, de nuevo, podemos aprovechar el constructor de String\ para crear un String legible por humanos a partir de esta misma secuencia. Teniendo en cuenta el hecho de que hemos codificado esta matriz de bytes en UTF_8, podemos seguir adelante y crear una nueva cadena de forma segura a partir de esto:

1
2
String utf8String = new String(bytes);
System.out.println(utf8String);

Nota: En lugar de codificarlos a través del método getBytes(), puedes también codificar los bytes a través del constructor String:

1
String utf8String = new String(bytes, StandardCharsets.UTF_8);

Esto ahora genera exactamente la misma cadena con la que comenzamos, pero codificada en UTF-8:

1
Šta radiš?

Codifique una cadena en UTF-8 con Java 7 StandardCharsets

Desde Java 7, hemos sido introducidos a la clase StandardCharsets, que tiene varios Charset disponibles como US_ASCII, ISO_8859_1, UTF_8 y UTF-16 entre otros.

Cada Charset tiene un método encode() y decode(), que acepta un CharBuffer (que implementa CharSequence, igual que String). En términos prácticos, esto significa que podemos introducir un String en los métodos encode() de un Charset.

El método encode() devuelve un ByteBuffer, que podemos volver a convertir fácilmente en una cadena.

Anteriormente, cuando usamos nuestro método getBytes(), almacenamos los bytes que obtuvimos en una matriz de bytes, pero cuando usamos la clase StandardCharsets, las cosas son un poco diferentes. Primero necesitamos usar una clase llamada ByteBuffer para almacenar nuestros bytes. Luego, necesitamos codificar y luego decodificar nuestros bytes recién asignados. Veamos cómo funciona esto en el código:

1
2
3
4
5
6
String japaneseString = "よろしくお願いします"; // Pleased to meet you.

ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(japaneseString);

String utf8String = new String(byteBuffer.array(), StandardCharsets.UTF_8);
System.out.println(utf8String);

Ejecutar este código da como resultado:

1
よろしくお願いします

Codifique una cadena en UTF-8 con Apache Commons

El paquete Apache Commons Codec contiene codificadores y decodificadores simples para varios formatos como Base64 y Hexadecimal. Además de estos codificadores y decodificadores ampliamente utilizados, el paquete de códec también mantiene una colección de utilidades de codificación fonética.

Para que podamos usar Apache Commons Codec, debemos agregarlo a nuestro proyecto como una dependencia externa.

Usando Maven, agreguemos la dependencia commons-codec a nuestro archivo pom.xml:

1
2
3
4
5
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>

Alternativamente, si estás usando Gradle:

1
compile 'commons-codec:commons-codec:1.15'

Ahora, podemos utilizar las clases de utilidad de Apache Commons y, como de costumbre, aprovecharemos la clase StringUtils.

Nos permite convertir cadenas a y desde bytes usando varias codificaciones requeridas por la especificación de Java. Esta clase es segura para nulos y subprocesos, por lo que tenemos una capa adicional de protección cuando trabajamos con cadenas.

Para codificar un String a UTF-8 con la clase StringUtils de Apache Common, podemos usar el método getBytesUtf8(), que funciona de manera muy similar al método getBytes() con un Charset especificado:

1
2
3
4
String germanString = "Wie heißen Sie?"; // What's your name?
byte[] bytes = StringUtils.getBytesUtf8(germanString);
String utf8String = StringUtils.newStringUtf8(bytes);
System.out.println(utf8String);

Esto resulta en:

1
Wie heißen Sie?

O, puede usar la clase StringUtils regular de la dependencia commons-lang3:

1
2
3
4
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

Si estás usando Gradle:

1
implementation group: 'org.apache.commons', name: 'commons-lang3', version: ${version}

Y ahora, podemos usar el mismo enfoque que con las cadenas normales:

1
2
3
4
String germanString = "Wie heißen Sie?"; // What's your name?
byte[] bytes = StringUtils.getBytes(germanString, StandardCharsets.UTF_8);
String utf8String = StringUtils.toEncodedString(bytes, StandardCharsets.UTF_8);
System.out.println(utf8String);

Sin embargo, este enfoque es seguro para subprocesos y nulos:

1
Wie heißen Sie?

Conclusión

En este tutorial, hemos echado un vistazo a cómo codificar una cadena Java en UTF-8. Hemos echado un vistazo a algunos enfoques: crear manualmente una cadena usando getBytes() y manipularlos, la clase StandardCharsets de Java 7 y Apache Commons. mons.