Conversión de una matriz de bytes en una cadena (Java)

85

Estoy escribiendo una aplicación web en Google App Engine. Permite a las personas editar básicamente código html que se almacena como un .htmlarchivo en el blobstore.

Estoy usando fetchData para devolver uno byte[]de todos los caracteres del archivo. Estoy intentando imprimir en un html para que el usuario edite el código html. ¡Todo funciona muy bien!

Este es mi único problema ahora:

La matriz de bytes tiene algunos problemas al volver a convertir a una cadena. Las citas inteligentes y un par de personajes están saliendo con un aspecto extraño. (? o símbolos japoneses, etc.) Específicamente, son varios bytes que veo que tienen valores negativos que están causando el problema.

Las comillas tipográficas vuelven como -108y -109en la matriz de bytes. ¿Por qué es esto y cómo puedo decodificar los bytes negativos para mostrar la codificación de caracteres correcta?

Josh
fuente
Duplicado de stackoverflow.com/questions/1536054/…
james.garriss
Hola, sé que es una publicación muy antigua, pero estoy enfrentando problemas similares. Estoy creando un proxy man-in-the-middle para ssl. El problema al que me enfrento es el mismo que el tuyo. Escucho el socket y pongo los datos en InputStreamy luego en byte[]. Ahora, cuando trato de convertir el byte[]en String (necesito usar el cuerpo de respuesta para los ataques), obtengo personajes realmente divertidos llenos de comillas inteligentes y signos de interrogación y demás. Creo que la suya problema es igual que la mía, ya que ambos se trata de htmlen byte[]. ¿Puedes darme un consejo?
Parul S
Por cierto, fui hasta el punto de encontrar la codificación de mi sistema usando Sytem.properties y encontré que era "Cp1252". Ahora, usé String str=new String(buffer, "Cp1252");pero no ayuda.
Parul S

Respuestas:

141

La matriz de bytes contiene caracteres en una codificación especial (que debe saber). La forma de convertirlo en una cadena es:

String decoded = new String(bytes, "UTF-8");  // example for one encoding type

Por cierto: los bytes sin procesar que aparecen pueden aparecer como decimales negativos solo porque el tipo de datos java byteestá firmado, cubre el rango de -128 a 127.


-109 = 0x93: Control Code "Set Transmit State"

El valor (-109) es un carácter de control no imprimible en UNICODE. Entonces, UTF-8 no es la codificación correcta para ese flujo de caracteres.

0x93en "Windows-1252" es la "cita inteligente" que está buscando, por lo que el nombre Java de esa codificación es "Cp1252". La siguiente línea proporciona un código de prueba:

System.out.println(new String(new byte[]{-109}, "Cp1252")); 
Andreas Dolk
fuente
5
Intenté usar UTF-8 y todavía me salió como? ¿Por qué no está encontrando un mapeo para esos valores negativos?
Josh
Sin embargo, 0x93 es un byte de continuación válido en UTF-8; la presencia de ese byte solo descarta que sea UTF-8 si no viene después de un byte con los dos primeros bits establecidos.
Nick Johnson
1
@Josh Andreas explica por qué, porque el bytetipo de datos de Java está firmado. Los valores 'negativos' son solo bytes con el conjunto de bytes más significativo. También explica cuál es el conjunto de caracteres más probable que debería usar: Windows-1252. Sin embargo, debe saber qué juego de caracteres usar según el contexto o la convención, sin tener que adivinar.
Nick Johnson
25

Java 7 y superior

También puede pasar la codificación deseada al Stringconstructor como una Charsetconstante de StandardCharsets . Esto puede ser más seguro que pasar la codificación como a String, como se sugiere en las otras respuestas.

Por ejemplo, para codificación UTF-8

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);
davnicwil
fuente
1
Esta es una repetición de una respuesta de 2011. -1
james.garriss
2
@ james.garriss No creo que lo sea, en la medida en que solo menciono un nuevo constructor introducido en java 7 que permite que la codificación se pase como una constante, que en mi opinión es más agradable y más segura que la api anterior mencionado en las respuestas anteriores donde la codificación se pasó como una Cadena, en todo caso.
davnicwil
11

Puedes probar esto.

String s = new String(bytearray);
Muhammad Aamir Ali
fuente
9
Puedes intentarlo ... pero fallará en casi todos los casos.
Raedwald
5
public class Main {

    /**
     * Example method for converting a byte to a String.
     */
    public void convertByteToString() {

        byte b = 65;

        //Using the static toString method of the Byte class
        System.out.println(Byte.toString(b));

        //Using simple concatenation with an empty String
        System.out.println(b + "");

        //Creating a byte array and passing it to the String constructor
        System.out.println(new String(new byte[] {b}));

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Main().convertByteToString();
    }
}

Salida

65
65
A
Adi Sembiring
fuente
5
public static String readFile(String fn)   throws IOException 
{
    File f = new File(fn);

    byte[] buffer = new byte[(int)f.length()];
    FileInputStream is = new FileInputStream(fn);
    is.read(buffer);
    is.close();

    return  new String(buffer, "UTF-8"); // use desired encoding
}
Craig
fuente
3
Este código filtrará un recurso si readarroja una excepción.
Raedwald
4

yo sugiero Arrays.toString(byte_array);

Depende de tu propósito. Por ejemplo, quería guardar una matriz de bytes exactamente como el formato que puede ver en el momento de la depuración que es algo como esto: [1, 2, 3]Si desea guardar exactamente el mismo valor sin convertir los bytes a formato de caracteres, Arrays.toString (byte_array)haga esto. Pero si desea guardar caracteres en lugar de bytes, debe usar String s = new String(byte_array). En este caso, ses equivalente a [1, 2, 3]en formato de carácter.

Preguntador
fuente
¿Puede darnos más información sobre por qué está sugiriendo esto? (¿Resolverá el problema? ¿Puede decir por qué lo resuelve?) ¡Gracias!
Dean J
Depende de tu propósito. Por ejemplo, quería guardar una matriz de bytes exactamente como el formato que puede ver en el momento de la depuración que es algo como esto: [1, 2, 3] Si desea guardar exactamente el mismo valor sin convertir los bytes a formato de caracteres, Arrays.toString (byte_array) hace esto. Pero si desea guardar caracteres en lugar de bytes, debe usar String s = new String (byte_array). En este caso, s es equivalente a [1, 2, 3] en formato de carácter.
Interlocutor
@sas, debe agregar esta información a su propia respuesta (editándola) en lugar de como un comentario. En general, en SO siempre debe tener en cuenta que los comentarios pueden eliminarse en cualquier momento; la información realmente importante debe estar en la respuesta misma.
Jeen Broekstra
3

La respuesta anterior de Andreas_D es buena. Solo voy a agregar que donde sea que muestre la salida, habrá una fuente y una codificación de caracteres y es posible que no admita algunos caracteres.

Para averiguar si es Java o su pantalla el problema, haga esto:

    for(int i=0;i<str.length();i++) {
        char ch = str.charAt(i);
        System.out.println(i+" : "+ch+" "+Integer.toHexString(ch)+((ch=='\ufffd') ? " Unknown character" : ""));
    }

Java habrá asignado cualquier carácter que no pueda entender a 0xfffd, el carácter oficial para caracteres desconocidos. Si ve un '?' en la salida, pero no está asignado a 0xfffd, es su fuente de visualización o codificación el problema, no Java.

Simón G.
fuente