Como converter matriz de bytes em string e vice-versa?

247

Eu tenho que converter uma matriz de bytes em string no Android, mas minha matriz de bytes contém valores negativos.

Se eu converter essa sequência novamente em matriz de bytes, os valores que estou recebendo são diferentes dos valores originais da matriz de bytes.

O que posso fazer para obter uma conversão adequada? O código que estou usando para fazer a conversão é o seguinte:

// 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);

Estou preso neste problema.

Jyotsna
fonte
3
Por que você está tentando converter dados binários arbitrários em uma String em primeiro lugar? Além de todos os problemas de charset que as respostas já mencionam, também há o fato de que você está abusando de String se fizer isso. O que há de errado em usar a byte[]para seus dados binários e Stringseu texto?
Joachim Sauer
8
@ Joachim - às vezes você tem ferramentas externas que podem fazer coisas como armazenar strings. Você deseja transformar uma matriz de bytes em uma string (codificada de alguma maneira) nesse caso.
111711 James Moore

Respostas:

377

Sua matriz de bytes deve ter alguma codificação. A codificação não pode ser ASCII se você tiver valores negativos. Depois de descobrir isso, você pode converter um conjunto de bytes em uma String usando:

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

Há um monte de codificações que você pode usar, olhada na classe Charset nos javadocs Sun .

omerkudat
fonte
4
@MauricePerry, você pode explicar por que não funciona UTF-8?
Asif Mushtaq 31/03
12
@Desconhecido porque o UTF-8 codifica alguns caracteres como cadeias de caracteres de 2 ou 3 bytes. Nem toda matriz de bytes é uma sequência de caracteres codificada em UTF-8 válida. A ISO-8859-1 seria uma escolha melhor: aqui cada caractere é codificado como um byte.
Maurice Perry
1
Isso pode funcionar, mas você deve evitar o uso do construtor String a todo custo.
hfontanez
mapear um byte para um caractere (com 8859-1) e sem manipulação de exceções (com nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
iman
1
desde o Java 1.7, você pode usar a nova String (bytes, StandardCharsets.UTF_8)
ihebiheb
101

A "conversão adequada" entre byte[]e Stringdeve declarar explicitamente a codificação que você deseja usar. Se você começar com um byte[]e ele não contiver dados de texto, não haverá "conversão adequada". Strings são para texto, byte[]são para dados binários, e a única coisa realmente sensata a fazer é evitar conversão entre eles, a menos que seja absolutamente necessário.

Se você realmente precisa usar a Stringpara armazenar dados binários, a maneira mais segura é usar a codificação Base64 .

Michael Borgwardt
fonte
1
Sim, a codificação de caracteres é algo que você precisa saber para converter entre seqüências de caracteres e bytes.
Raedwald
4
Base64 e você salvou minha vida
mstzn
2
A codificação Base64 resolveu meu problema. UTF-8 não funciona para todas as entradas
Al-Alamin
37

O principal problema é que você involuntariamente está usando um conjunto de caracteres para o qual:

 bytes != encode(decode(bytes))

em alguns casos. UTF-8 é um exemplo desse conjunto de caracteres. Especificamente, certas seqüências de bytes não são codificações válidas em UTF-8. Se o decodificador UTF-8 encontrar uma dessas seqüências, é possível descartar os bytes incorretos ou decodificá-los como o ponto de código Unicode para "esse caractere não existe". Naturalmente, quando você tentar codificar os caracteres como bytes, o resultado será diferente.

A solução é:

  1. Seja explícito sobre a codificação de caracteres que você está usando; ou seja, use um construtor String e String.toByteArraymétodo com um conjunto de caracteres explícito.
  2. Use o conjunto de caracteres correto para seus dados de bytes ... ou alternativamente um (como "Latin-1", em que todas as seqüências de bytes são mapeadas para caracteres Unicode válidos.
  3. Se seus bytes são (realmente) dados binários e você deseja transmiti-los / recebê-los em um canal "baseado em texto", use algo como a codificação Base64 ... projetada para esse fim .
Stephen C
fonte
1
Obrigado pela dica de usar a codificação "Latin-1"!
Gonzo 31/05
31

Nós só precisamos construir um novo Stringcom a matriz: http://www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

Os bytes da sequência resultante diferem dependendo do conjunto de caracteres que você usa. new String (bytes) e new String (bytes, Charset.forName ("utf-8")) e new String (bytes, Charset.forName ("utf-16")) terão todas diferentes matrizes de bytes quando você chamar String # getBytes () (dependendo do conjunto de caracteres padrão)

Ravindranath Akila
fonte
9
Não. Os bytes da sequência resultante diferem dependendo do conjunto de caracteres que você usa. new String(bytes)e new String(bytes, Charset.forName("utf-8"))e new String(bytes, Charset.forName("utf-16"))todos terão diferentes matrizes de bytes quando você chamar String#getBytes()(dependendo do conjunto de caracteres padrão)
NS du Toit
1
Enganador. Os chars (e, portanto, o texto exibido) do resultado Stringdiferem ao decodificar de forma bytesdiferente. A conversão de volta para bytes usando a codificação padrão (use String#getBytes("charset")para especificar o contrário) necessariamente será diferente porque converte entrada diferente. As strings não armazenam o byte[]que foram feitas, chars não têm uma codificação e a Stringnão a armazena de outra forma.
Zapl 24/05
14

Usar new String(byOriginal)e converter novamente em byte[]uso getBytes()não garante dois byte[]com valores iguais. Isto é devido a uma chamada para StringCoding.encode(..)que irá codificar o Stringpara Charset.defaultCharset(). Durante essa codificação, o codificador pode optar por substituir caracteres desconhecidos e fazer outras alterações. Portanto, o uso String.getBytes()pode não retornar uma matriz igual à que você passou originalmente para o construtor.

sfussenegger
fonte
9

Por que o problema: Como alguém já especificou: Se você inicia com um byte [] e, de fato, não contém dados de texto, não há "conversão adequada". Strings são para texto, byte [] é para dados binários, e a única coisa realmente sensata a fazer é evitar a conversão entre eles, a menos que você precise.

Eu estava observando esse problema quando tentava criar o byte [] a partir de um arquivo pdf, convertendo-o em String e, em seguida, pegando o String como entrada e convertendo-o novamente em arquivo.

Portanto, verifique se a lógica de codificação e decodificação é a mesma que eu fiz. Eu codifiquei explicitamente o byte [] para Base64 e decodifiquei para criar o arquivo novamente.

Use-case: Devido a algumas limitações que eu estava tentando enviado byte[]no request(POST)e o processo foi o seguinte:

Arquivo PDF >> Base64.encodeBase64 (byte []) >> String >> Solicitação de envio (POST) >> receive String >> Base64.decodeBase64 (byte []) >> create binary

Tente isso e isso funcionou para mim ..

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
fonte
6

Este trabalho é bom para mim:

String cd="Holding some value";

Convertendo de string para byte []:

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

Convertendo de byte [] para string:

cd = new sun.misc.BASE64Encoder().encode(cookie);
Conduziu
fonte
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
fonte
Isso não responde à pergunta.
James.garriss
Não respondeu à pergunta, mas foi útil +1
Lazy Ninja
5

Eu notei algo que não está em nenhuma das respostas. Você pode converter cada um dos bytes da matriz de bytes em caracteres e colocá-los em uma matriz de caracteres. Então a string é

new String(cbuf)
onde cbuf é a matriz char. Para converter de volta, faça um loop pela string que converte cada um dos caracteres em bytes para colocar em uma matriz de bytes, e essa matriz de bytes será a mesma que a primeira.


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
fonte
2

javax.xml.bind.DatatypeConverter deve fazê-lo:

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

Aqui estão alguns métodos que convertem uma matriz de bytes em uma string. Eu testei-os, eles funcionam bem.

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();

}
user2288580
fonte
2

Apesar de

new String(bytes, "UTF-8")

está correto, lança um UnsupportedEncodingExceptionque força você a lidar com uma exceção verificada. Você pode usar como alternativa outro construtor desde o Java 1.6 para converter uma matriz de bytes em String:

new String(bytes, StandardCharsets.UTF_8)

Este não lança nenhuma exceção.

A conversão de volta também deve ser feita com StandardCharsets.UTF_8:

"test".getBytes(StandardCharsets.UTF_8)

Novamente, você evita ter que lidar com exceções verificadas.

gil.fernandes
fonte
1

Consegui converter a matriz de bytes em uma string com 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
fonte
1

Embora a codificação base64 seja segura e possa-se argumentar "a resposta certa", cheguei aqui procurando uma maneira de converter uma matriz de bytes Java para / de uma String Java como está. Ou seja, onde cada membro da matriz de bytes permanece intacto em sua contraparte String, sem espaço extra necessário para codificação / transporte.

Esta resposta que descreve codificações transparentes de 8 bits foi muito útil para mim. eu useiISO-8859-1 terabytes de dados binários para converter com êxito (binário <-> String) sem os requisitos de espaço inflado necessários para uma codificação base64, portanto, é seguro para o meu caso de uso - YMMV.

Isso também foi útil para explicar quando / se você deve experimentar.

Reed Sandberg
fonte
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
fonte
Por quê? Por que passar pelo Base64 para converter um byte em uma String? A sobrecarga.
James.garriss
0

Aqui o código de trabalho.

            // 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
fonte
-1

Tente especificar um conjunto de caracteres de 8 bits nas duas conversões. ISO-8859-1, por exemplo.

Maurice Perry
fonte
-1

Leia os bytes de Stringuso ByteArrayInputStreame envolva-o com BufferedReaderChar Char em vez de Byte Stream, que converte os dados de bytes em 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();
        }

    }
}

A saída é:

Sajal é um bom garoto

Sajal Goyal
fonte
-1

Você pode usar o loop for simples para conversão:

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
fonte
-2
InputStream is = new FileInputStream("/home/kalt/Desktop/SUDIS/READY/ds.bin");
byte[] bytes = IOUtils.toByteArray(is);
Дмитрий Колтович
fonte
-3

Uma string é uma coleção de caracteres (16 bits não assinados). Portanto, se você converter números negativos em uma sequência, eles serão perdidos na tradução.

Sapo
fonte
1
-1: isso está incorreto. Enquanto 'byte' é um tipo assinado em Java, eles são tratados como não assinados pelo código da biblioteca que codifica e decodifica o conjunto de caracteres.
Stephen C
Um bom exemplo de como ter um tipo de dados não assinado de 8 bits é realmente uma boa idéia para se ter em um idioma. Evita confusão desnecessária; ^)
Toad
Seja cuidadoso ao presumir que um char Java será 16 bits, por causa do Java UTF-16, que pode expandir até 32 bits
Joe Plante
1
@ Carregue sim, alguns caracteres Unicode quando armazenados como UTF-16 ocupam dois pontos de código, ou seja, 32 bits. O mesmo acontece em UTF-8: alguns caracteres usam dois / três / quatro pontos de código, ou seja, 16/24/32 bits. De fato, é exatamente disso que trata UTF (ou seja, UTF! = Unicode).
CAFxX 01/12/12
1
@Carga, você obteria o primeiro substituto - ou seja, apenas a primeira "metade" do personagem. Veja os documentos do método String.charAt e da classe 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
fonte
Passe a codificação Charset como argumento para getBytes #
Shyam Sreenivasan
1
Você pode considerar aprofundar esta resposta com uma explicação além do código.
Charlie Schliesser