¿Cómo convertir una matriz de bytes a una cadena y viceversa?

248

Tengo que convertir una matriz de bytes a una cadena en Android, pero mi matriz de bytes contiene valores negativos.

Si convierto esa cadena de nuevo en matriz de bytes, los valores que obtengo son diferentes de los valores originales de la matriz de bytes.

¿Qué puedo hacer para obtener la conversión adecuada? El código que estoy usando para hacer la conversión es el siguiente:

// Code to convert byte arr to str:
byte[] by_original = {0,1,-2,3,-4,-5,6};
String str1 = new String(by_original);
System.out.println("str1 >> "+str1);

// Code to convert str to byte arr:
byte[] by_new = str1.getBytes();
for(int i=0;i<by_new.length;i++) 
System.out.println("by1["+i+"] >> "+str1);

Estoy atrapado en este problema.

Jyotsna
fuente
3
¿Por qué estás tratando de convertir datos binarios arbitrarios a una cadena en primer lugar? Además de todos los problemas de juego de caracteres que las respuestas ya mencionan, también existe el hecho de que estás abusando de String si haces esto. ¿Qué tiene de malo usar a byte[]para sus datos binarios y Stringpara su texto?
Joachim Sauer
8
@Joachim: a veces tienes herramientas externas que pueden hacer cosas como almacenar cadenas. Desea poder convertir una matriz de bytes en una cadena (codificada de alguna manera) en ese caso.
James Moore

Respuestas:

377

Su conjunto de bytes debe tener algo de codificación. La codificación no puede ser ASCII si tiene valores negativos. Una vez que descubras eso, puedes convertir un conjunto de bytes a una Cadena usando:

byte[] bytes = {...}
String str = new String(bytes, "UTF-8"); // for UTF-8 encoding

Hay un montón de codificaciones que se pueden utilizar, vistazo a la clase de Caracteres en los javadocs sol .

omerkudat
fuente
44
@MauricePerry, ¿puede explicar por qué no funcionará UTF-8?
Asif Mushtaq
12
@UnKnown porque UTF-8 codifica algunos caracteres como cadenas de 2 o 3 bytes. No todos los conjuntos de bytes son una cadena válida codificada en UTF-8. ISO-8859-1 sería una mejor elección: aquí cada carácter se codifica como un byte.
Maurice Perry
1
Esto podría funcionar, pero debe evitar el uso del constructor de cadenas a toda costa.
hfontanez
para asignar un byte a un carácter (con 8859-1) y sin manejo de excepciones (con nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
Iman
1
desde Java 1.7, puede usar una nueva cadena (bytes, StandardCharsets.UTF_8)
ihebiheb
101

La "conversión adecuada" entre byte[]y Stringes indicar explícitamente la codificación que desea utilizar. Si comienza con a byte[]y, de hecho, no contiene datos de texto, no hay una "conversión adecuada". Strings son para texto, byte[]es para datos binarios, y lo único realmente sensato es evitar la conversión entre ellos a menos que sea absolutamente necesario.

Si realmente debe usar a Stringpara contener datos binarios, entonces la forma más segura es usar la codificación Base64 .

Michael Borgwardt
fuente
1
Sí, la codificación de caracteres es algo que debe saber para convertir entre cadenas y bytes.
Raedwald
44
Base64 y me salvaste la vida
mstzn
2
La codificación Base64 resolvió mi problema. UTF-8 no funcionó para todas las entradas
Al-Alamin
37

El problema raíz es (creo) que inconscientemente estás usando un juego de caracteres para el cual:

 bytes != encode(decode(bytes))

en algunos casos. UTF-8 es un ejemplo de tal conjunto de caracteres. Específicamente, ciertas secuencias de bytes no son codificaciones válidas en UTF-8. Si el decodificador UTF-8 encuentra una de estas secuencias, es probable que descarte los bytes ofensivos o los decodifique como el punto de código Unicode para "no tener ese carácter". Naturalmente, cuando intentes codificar los caracteres como bytes, el resultado será diferente.

La solucion es:

  1. Sea explícito sobre la codificación de caracteres que está utilizando; es decir, usar un constructor de cadenas y un String.toByteArraymétodo con un juego de caracteres explícito.
  2. Utilice el conjunto de caracteres correcto para sus datos de bytes ... o alternativamente uno (como "Latin-1" donde todas las secuencias de bytes se asignan a caracteres Unicode válidos.
  3. Si sus bytes son (realmente) datos binarios y desea poder transmitirlos / recibirlos a través de un canal "basado en texto", use algo como la codificación Base64 ... que está diseñada para este propósito .
Stephen C
fuente
1
¡Gracias por el consejo de usar la codificación "Latin-1"!
Gonzo
31

Solo necesitamos construir una nueva Stringcon la matriz: http://www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

Los bytes de la cadena resultante difieren según el juego de caracteres que utilice. la nueva Cadena (bytes) y la nueva Cadena (bytes, Charset.forName ("utf-8")) y la nueva Cadena (bytes, Charset.forName ("utf-16")) tendrán matrices de bytes diferentes cuando llame a la Cadena # getBytes () (según el juego de caracteres predeterminado)

Ravindranath Akila
fuente
99
No. Los bytes de la cadena resultante difieren según el juego de caracteres que use. new String(bytes)y new String(bytes, Charset.forName("utf-8"))y new String(bytes, Charset.forName("utf-16"))todos tendrán diferentes matrices de bytes cuando se llama String#getBytes()(dependiendo del juego de caracteres por defecto)
NS du Toit
1
Engañoso. La chars (y por lo tanto el texto que se muestra) del resultado Stringdifiere cuando se decodifica de manera bytesdiferente. La conversión de vuelta a bytes usando la codificación predeterminada (use String#getBytes("charset")para especificar lo contrario) necesariamente diferirá porque convierte una entrada diferente. Las cadenas no almacenan el material del byte[]que están hechas, chars no tienen codificación y a Stringno lo almacenan de otra manera.
zapl
14

Usar new String(byOriginal)y volver a byte[]usar getBytes()no garantiza dos byte[]con valores iguales. Esto se debe a una llamada a la StringCoding.encode(..)que codificará el Stringto Charset.defaultCharset(). Durante esta codificación, el codificador podría elegir reemplazar caracteres desconocidos y hacer otros cambios. Por lo tanto, el uso String.getBytes()podría no devolver una matriz igual como la que originalmente le pasó al constructor.

sfussenegger
fuente
9

¿Por qué fue el problema? Como alguien ya especificó: si comienza con un byte [] y, de hecho, no contiene datos de texto, no hay una "conversión adecuada". Las cadenas son para texto, el byte [] es para datos binarios, y lo único realmente sensato es evitar la conversión entre ellas a menos que sea absolutamente necesario.

Estaba observando este problema cuando intentaba crear un byte [] a partir de un archivo pdf y luego convertirlo a String y luego tomar el String como entrada y volver a convertirlo en archivo.

Así que asegúrese de que su lógica de codificación y decodificación sea la misma que yo. Codifiqué explícitamente el byte [] a Base64 y lo decodifiqué para crear el archivo nuevamente.

De casos de uso: Debido a algunas limitaciones que intentaba enviado byte[]en request(POST)y el proceso fue el siguiente:

Archivo PDF >> Base64.encodeBase64 (byte []) >> String >> Enviar solicitud (POST) >> recibir String >> Base64.decodeBase64 (byte []) >> crear binario

Prueba esto y esto funcionó para mí ...

File file = new File("filePath");

        byte[] byteArray = new byte[(int) file.length()];

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            fileInputStream.read(byteArray);

            String byteArrayStr= new String(Base64.encodeBase64(byteArray));

            FileOutputStream fos = new FileOutputStream("newFilePath");
            fos.write(Base64.decodeBase64(byteArrayStr.getBytes()));
            fos.close();
        } 
        catch (FileNotFoundException e) {
            System.out.println("File Not Found.");
            e.printStackTrace();
        }
        catch (IOException e1) {
            System.out.println("Error Reading The File.");
            e1.printStackTrace();
        }
Rupesh
fuente
6

Esto funciona bien para mi:

String cd="Holding some value";

Conversión de cadena a byte []:

byte[] cookie = new sun.misc.BASE64Decoder().decodeBuffer(cd);

Conversión de byte [] a cadena:

cd = new sun.misc.BASE64Encoder().encode(cookie);
LED
fuente
5
private static String toHexadecimal(byte[] digest){
        String hash = "";
    for(byte aux : digest) {
        int b = aux & 0xff;
        if (Integer.toHexString(b).length() == 1) hash += "0";
        hash += Integer.toHexString(b);
    }
    return hash;
}
sdelvalle57
fuente
1
Esto no responde la pregunta.
james.garriss
No responde la pregunta pero fue útil +1
Lazy Ninja
5

Noté algo que no está en ninguna de las respuestas. Puede convertir cada uno de los bytes de la matriz de bytes en caracteres y colocarlos en una matriz de caracteres. Entonces la cuerda es

new String(cbuf)
donde cbuf es la matriz de caracteres. Para volver a convertir, recorra la cadena convirtiendo cada uno de los caracteres en bytes para colocar en una matriz de bytes, y esta matriz de bytes será la misma que la primera.


public class StringByteArrTest {

    public static void main(String[] args) {
        // put whatever byte array here
        byte[] arr = new byte[] {-12, -100, -49, 100, -63, 0, -90};
        for (byte b: arr) System.out.println(b);
        // put data into this char array
        char[] cbuf = new char[arr.length];
        for (int i = 0; i < arr.length; i++) {
            cbuf[i] = (char) arr[i];
        }
        // this is the string
        String s = new String(cbuf);
        System.out.println(s);

        // converting back
        byte[] out = new byte[s.length()];
        for (int i = 0; i < s.length(); i++) {
            out[i] = (byte) s.charAt(i);
        }
        for (byte b: out) System.out.println(b);
    }

}
Leonid
fuente
2

javax.xml.bind.DatatypeConverter Deberías hacerlo:

byte [] b = javax.xml.bind.DatatypeConverter.parseHexBinary("E62DB");
String s = javax.xml.bind.DatatypeConverter.printHexBinary(b);
Wolfgang Kaisers
fuente
2

Aquí hay algunos métodos que convierten una matriz de bytes en una cadena. Los he probado, funcionan bien.

public String getStringFromByteArray(byte[] settingsData) {

    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(settingsData);
    Reader reader = new BufferedReader(new InputStreamReader(byteArrayInputStream));
    StringBuilder sb = new StringBuilder();
    int byteChar;

    try {
        while((byteChar = reader.read()) != -1) {
            sb.append((char) byteChar);
        }
    }
    catch(IOException e) {
        e.printStackTrace();
    }

    return sb.toString();

}

public String getStringFromByteArray(byte[] settingsData) {

    StringBuilder sb = new StringBuilder();
    for(byte willBeChar: settingsData) {
        sb.append((char) willBeChar);
    }

    return sb.toString();

}
usuario2288580
fuente
2

Aunque

new String(bytes, "UTF-8")

es correcto, arroja un UnsupportedEncodingExceptionque te obliga a lidiar con una excepción marcada. Como alternativa, puede usar otro constructor desde Java 1.6 para convertir una matriz de bytes en String:

new String(bytes, StandardCharsets.UTF_8)

Este no arroja ninguna excepción.

La conversión de regreso también debe hacerse con StandardCharsets.UTF_8:

"test".getBytes(StandardCharsets.UTF_8)

Nuevamente, evitas tener que lidiar con excepciones marcadas.

gil.fernandes
fuente
1

Logré convertir byte array a una cadena con este método:

public static String byteArrayToString(byte[] data){
    String response = Arrays.toString(data);

    String[] byteValues = response.substring(1, response.length() - 1).split(",");
    byte[] bytes = new byte[byteValues.length];

    for (int i=0, len=bytes.length; i<len; i++) {
        bytes[i] = Byte.parseByte(byteValues[i].trim());
    }

    String str = new String(bytes);
    return str.toLowerCase();
}
lxknvlk
fuente
1

Si bien la codificación base64 es segura y se podría argumentar "la respuesta correcta", llegué aquí buscando una manera de convertir una matriz de bytes de Java a / desde una cadena de Java tal como está. Es decir, donde cada miembro de la matriz de bytes permanece intacto en su contraparte de cadena, sin espacio adicional requerido para la codificación / transporte.

Esta respuesta que describe codificaciones transparentes de 8 bits fue muy útil para mí. Utilicé ISO-8859-1en terabytes de datos binarios para convertir con éxito (Binary <-> String) sin los requisitos de espacio inflado necesarios para una codificación base64, por lo que es seguro para mi caso de uso: YMMV.

Esto también fue útil para explicar cuándo / si debe experimentar.

Reed Sandberg
fuente
0
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;    

private static String base64Encode(byte[] bytes)
{
    return new BASE64Encoder().encode(bytes);
}

private static byte[] base64Decode(String s) throws IOException
{
    return new BASE64Decoder().decodeBuffer(s);
}
Feng Zhang
fuente
¿Por qué? ¿Por qué pasaría por Base64 para convertir un byte en una cadena? La sobrecarga.
james.garriss
0

Aquí el código de trabajo.

            // Encode byte array into string . TemplateBuffer1 is my bytearry variable.

        String finger_buffer = Base64.encodeToString(templateBuffer1, Base64.DEFAULT);
        Log.d(TAG, "Captured biometric device->" + finger_buffer);


        // Decode String into Byte Array. decodedString is my bytearray[] 
        decodedString = Base64.decode(finger_buffer, Base64.DEFAULT);
sudharsan chandrasekaran
fuente
-1

Intente especificar un juego de caracteres de 8 bits en ambas conversiones. ISO-8859-1 por ejemplo.

Maurice Perry
fuente
-1

Lea los bytes del Stringuso ByteArrayInputStreamy envuélvalos con BufferedReaderChar Stream en lugar de Byte Stream, que convierte los datos de bytes en String.

package com.cs.sajal;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class TestCls {

    public static void main(String[] args) {

        String s=new String("Sajal is  a good boy");

        try
        {
        ByteArrayInputStream bis;
        bis=new ByteArrayInputStream(s.getBytes("UTF-8"));

        BufferedReader br=new BufferedReader(new InputStreamReader(bis));
        System.out.println(br.readLine());

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

    }
}

Salida es:

Sajal es un buen chico

Sajal Goyal
fuente
-1

Puede usar simple for loop para la conversión:

public void byteArrToString(){
   byte[] b = {'a','b','$'};
   String str = ""; 
   for(int i=0; i<b.length; i++){
       char c = (char) b[i];
       str+=c;
   }
   System.out.println(str);
}
amoljdv06
fuente
-2
InputStream is = new FileInputStream("/home/kalt/Desktop/SUDIS/READY/ds.bin");
byte[] bytes = IOUtils.toByteArray(is);
Дмитрий Колтович
fuente
-3

Una cadena es una colección de caracteres (16 bits sin signo). Entonces, si va a convertir números negativos en una cadena, se perderán en la traducción.

Sapo
fuente
1
-1: Esto es incorrecto. Si bien 'byte' es un tipo con signo en Java, el código de la biblioteca que codifica y decodifica el juego de caracteres los trata como no firmados.
Stephen C
Un buen ejemplo de por qué tener un tipo de datos de 8 bits sin signo es realmente una buena idea tener en un idioma. Evita confusión innecesaria; ^)
Sapo
Tenga cuidado al suponer que un carácter Java será de 16 bits, debido a UTF-16 de Java, pueden expandirse hasta 32 bits
Joe Plante
1
@Toad en realidad sí, algunos caracteres Unicode cuando se almacenan como UTF-16 ocupan dos puntos de código, es decir, 32 bits. Lo mismo sucede en UTF-8: algunos caracteres usan dos / tres / cuatro puntos de código, es decir, 16/24/32 bits. De hecho, de eso se trata exactamente UTF (es decir, UTF! = Unicode).
CAFxX
1
@Toad obtendrías el primer sustituto, es decir, solo la primera "mitad" del personaje. Mire los documentos para el método String.charAt y la clase Character .
CAFxX
-3
public class byteString {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        String msg = "Hello";
        byte[] buff = new byte[1024];
        buff = msg.getBytes("UTF-8");
        System.out.println(buff);
        String m = new String(buff);
        System.out.println(m);


    }

}
Shyam Sreenivasan
fuente
Pase The Charset Encoding como argumento para getBytes
Shyam Sreenivasan
1
Es posible que desee considerar desarrollar esta respuesta con una explicación además del código.
Charlie Schliesser