Cómo convertir cadenas hacia y desde matrices de bytes UTF8 en Java

239

En Java, tengo una cadena y quiero codificarla como una matriz de bytes (en UTF8, o alguna otra codificación). Alternativamente, tengo una matriz de bytes (en alguna codificación conocida) y quiero convertirla en una cadena de Java. ¿Cómo hago estas conversiones?

mcherm
fuente

Respuestas:

355

Convierte de String a byte []:

String s = "some text here";
byte[] b = s.getBytes(StandardCharsets.UTF_8);

Convertir de byte [] a String:

byte[] b = {(byte) 99, (byte)97, (byte)116};
String s = new String(b, StandardCharsets.US_ASCII);

Por supuesto, debe usar el nombre de codificación correcto. Mis ejemplos utilizaron US-ASCII y UTF-8, las dos codificaciones más comunes.

mcherm
fuente
30
US-ASCII en realidad no es una codificación muy común hoy en día. Windows-1252 e ISO-8859-1 (que son supersets de ASCII) están mucho más extendidos.
Michael Borgwardt
11
En realidad, me parece bastante común en mi trabajo. A menudo leo secuencias de bytes que pueden haberse guardado como Windows-1252 o ISO-8859-1 o incluso simplemente como "salida de ese programa heredado que hemos tenido durante los últimos 10 años", pero que contienen bytes garantizados como válidos. Caracteres ASCII de EE. UU. También a menudo tengo el requisito de GENERAR dichos archivos (para consumo por código que puede o no puede manejar caracteres que no son ASCII. Básicamente, US-ASCII es el "máximo común denominador" de muchas piezas de software.
mcherm
1
Sin embargo, este método no informará ningún problema en la conversión. Esto puede ser lo que quieres. De lo contrario, se recomienda utilizar CharsetEncoder en su lugar.
Michael Piefel
77
@Pacerier porque los documentos para Charset enumeran "UTF-8" como uno de los charsets estándar. Creo que su ortografía también es aceptada, pero seguí con lo que decían los documentos.
mcherm
20
Desde JDK7 puede usar StandardCharsets.UTF_8 docs.oracle.com/javase/7/docs/api/java/nio/charset/…
Rafael Membrives
95

Aquí hay una solución que evita realizar la búsqueda de Charset para cada conversión:

import java.nio.charset.Charset;

private final Charset UTF8_CHARSET = Charset.forName("UTF-8");

String decodeUTF8(byte[] bytes) {
    return new String(bytes, UTF8_CHARSET);
}

byte[] encodeUTF8(String string) {
    return string.getBytes(UTF8_CHARSET);
}
Mike Leonhard
fuente
44
@mcherm: Incluso si la diferencia de rendimiento es pequeña, prefiero usar objetos (Charset, URL, etc.) sobre sus formas de cadena cuando sea posible.
Bart van Heukelom
77
Nota: Cadena pública "Since 1.6" (byte [] bytes, Charset charset)
leo
1
Con respecto a "evita realizar la búsqueda de Charset para cada conversión" ... por favor, cita alguna fuente. ¿No está java.nio.charset.Charset construido sobre String.getBytes y, por lo tanto, tiene más sobrecarga que String.getBytes?
Pacerier
2
Los documentos dicen: "El comportamiento de este método cuando esta cadena no se puede codificar en el juego de caracteres dado no está especificado. La clase CharsetEncoder se debe usar cuando se requiere más control sobre el proceso de codificación".
paiego
24
Nota: desde Java 1.7, puede usarlo StandardCharsets.UTF_8de manera constante para acceder al juego de caracteres UTF-8.
Kat
17
String original = "hello world";
byte[] utf8Bytes = original.getBytes("UTF-8");
Jorge Ferreira
fuente
¡Gracias! Lo escribí nuevamente agregando la otra dirección de conversión.
mcherm
1
@smink El guión no es opcional. Esto debería usar "UTF-8"
Mel Nicholson
14

Puede convertir directamente a través del constructor String (byte [], String) y el método getBytes (String). Java expone los juegos de caracteres disponibles a través de la clase Charset . La documentación de JDK enumera codificaciones compatibles .

El 90% del tiempo, tales conversiones se realizan en transmisiones, por lo que usaría las clases Reader / Writer . No descodificaría incrementalmente utilizando los métodos de cadena en secuencias de bytes arbitrarias; se dejaría abierto a errores que involucren caracteres multibyte.

McDowell
fuente
¿Puedes elaborar? Si mi aplicación codifica y decodifica cadenas UTF-8, ¿cuál es la preocupación con respecto a los caracteres multibytes?
raffian
@raffian Pueden ocurrir problemas si no transforma todos los datos de los personajes de una vez. Ver aquí para un ejemplo.
McDowell
12

Mi implementación de tomcat7 está aceptando cadenas como ISO-8859-1; a pesar del tipo de contenido de la solicitud HTTP. La siguiente solución funcionó para mí al intentar interpretar correctamente caracteres como 'é'.

byte[] b1 = szP1.getBytes("ISO-8859-1");
System.out.println(b1.toString());

String szUT8 = new String(b1, "UTF-8");
System.out.println(szUT8);

Al intentar interpretar la cadena como US-ASCII, la información del byte no se interpretó correctamente.

b1 = szP1.getBytes("US-ASCII");
System.out.println(b1.toString());
paiego
fuente
8
Para su información, a partir de Java 7, puede usar constantes para esos nombres de caracteres como StandardCharSets.UTF_8y StandardCharSets.ISO_8859_1.
Basil Bourque
Me salvó el día, trabajando absolutamente bien para la primera solución mencionada anteriormente.
Hassan Jamil
7

Como alternativa, se pueden usar StringUtils de Apache Commons.

 byte[] bytes = {(byte) 1};
 String convertedString = StringUtils.newStringUtf8(bytes);

o

 String myString = "example";
 byte[] convertedBytes = StringUtils.getBytesUtf8(myString);

Si tiene un juego de caracteres no estándar, puede usar getBytesUnchecked () o newString () en consecuencia.

vtor
fuente
44
Tenga en cuenta que este StringUtils proviene de Commons Codec , no Commons Lang.
Arend v. Reinersdorff
Sí, ¡un poquito de mierda! Para los usuarios de Gradle, Maven: "commons-codec: commons-codec: 1.10" (en el momento de la escritura). Esto también viene incluido como una dependencia con Apache POI, por ejemplo. ¡Aparte de eso, Apache Commons al rescate, como siempre!
Mike Rodent
2

Para decodificar una serie de bytes en un mensaje de cadena normal, finalmente lo hice funcionar con la codificación UTF-8 con este código:

/* Convert a list of UTF-8 numbers to a normal String
 * Usefull for decoding a jms message that is delivered as a sequence of bytes instead of plain text
 */
public String convertUtf8NumbersToString(String[] numbers){
    int length = numbers.length;
    byte[] data = new byte[length];

    for(int i = 0; i< length; i++){
        data[i] = Byte.parseByte(numbers[i]);
    }
    return new String(data, Charset.forName("UTF-8"));
}
Bouke Woudstra
fuente
1

Si está utilizando ASCII de 7 bits o ISO-8859-1 (un formato increíblemente común), entonces no tiene que crear un nuevo java.lang.String . Es mucho más eficiente simplemente convertir el byte en char:

Ejemplo de trabajo completo:

for (byte b : new byte[] { 43, 45, (byte) 215, (byte) 247 }) {
    char c = (char) b;
    System.out.print(c);
}

Si está no utiliza caracteres extendidos como un, AE, A, C, I, E y puede estar seguro de que los únicos valores transmitidos son de los primeros 128 caracteres Unicode, a continuación, este código también trabajo para UTF-8 y ASCII extendido (como cp-1252).

rev Pacerier
fuente
1

No puedo comentar pero no quiero comenzar un nuevo hilo. Pero esto no está funcionando. Un simple viaje de ida y vuelta:

byte[] b = new byte[]{ 0, 0, 0, -127 };  // 0x00000081
String s = new String(b,StandardCharsets.UTF_8); // UTF8 = 0x0000, 0x0000,  0x0000, 0xfffd
b = s.getBytes(StandardCharsets.UTF_8); // [0, 0, 0, -17, -65, -67] 0x000000efbfbd != 0x00000081

Necesitaría b [] la misma matriz antes y después de la codificación que no es (esto se refiere a la primera respuesta).

jschober
fuente
0
//query is your json   

 DefaultHttpClient httpClient = new DefaultHttpClient();
 HttpPost postRequest = new HttpPost("http://my.site/test/v1/product/search?qy=");

 StringEntity input = new StringEntity(query, "UTF-8");
 input.setContentType("application/json");
 postRequest.setEntity(input);   
 HttpResponse response=response = httpClient.execute(postRequest);
Ran Adler
fuente
¿La entidad de cadena convierte la 'consulta' en utf-8 o simplemente recuerda cuando adjunta la entidad?
SyntaxRules
0
Charset UTF8_CHARSET = Charset.forName("UTF-8");
String strISO = "{\"name\":\"א\"}";
System.out.println(strISO);
byte[] b = strISO.getBytes();
for (byte c: b) {
    System.out.print("[" + c + "]");
}
String str = new String(b, UTF8_CHARSET);
System.out.println(str);
Nitish Raj Srivastava
fuente
0
Reader reader = new BufferedReader(
    new InputStreamReader(
        new ByteArrayInputStream(
            string.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Макс Даниленко
fuente
-9

terriblemente tarde, pero acabo de encontrar este problema y esta es mi solución:

private static String removeNonUtf8CompliantCharacters( final String inString ) {
    if (null == inString ) return null;
    byte[] byteArr = inString.getBytes();
    for ( int i=0; i < byteArr.length; i++ ) {
        byte ch= byteArr[i]; 
        // remove any characters outside the valid UTF-8 range as well as all control characters
        // except tabs and new lines
        if ( !( (ch > 31 && ch < 253 ) || ch == '\t' || ch == '\n' || ch == '\r') ) {
            byteArr[i]=' ';
        }
    }
    return new String( byteArr );
}
savio
fuente
2
Primero, no es una conversión: es la eliminación de bytes no imprimibles. En segundo lugar, se supone que la codificación predeterminada del sistema operativo subyacente se basa realmente en ASCII para caracteres imprimibles (por ejemplo, no funcionará en Mainframes de IBM utilizando EBCDIC).
Isaac