Convertendo matriz de bytes em String (Java)

85

Estou escrevendo um aplicativo da web no Google app Engine. Ele permite que as pessoas basicamente editem o código html que é armazenado como um .htmlarquivo no blobstore.

Estou usando fetchData para retornar um byte[]de todos os caracteres no arquivo. Estou tentando imprimir em um html para que o usuário edite o código html. Tudo funciona muito bem!

Aqui está meu único problema agora:

A matriz de bytes está tendo alguns problemas ao converter de volta para uma string. Citações inteligentes e alguns personagens estão saindo do ar. (? 's ou símbolos japoneses etc.) Especificamente são vários bytes que estou vendo que têm valores negativos que estão causando o problema.

As aspas inteligentes estão voltando como -108e -109na matriz de bytes. Por que isso acontece e como posso decodificar os bytes negativos para mostrar a codificação de caracteres correta?

Josh
fonte
Duplicado de stackoverflow.com/questions/1536054/…
james.garriss
Olá, eu sei que é um post muito antigo, mas estou enfrentando problemas semelhantes. Estou criando um proxy man-in-the-middle para SSL. O problema que estou enfrentando é o mesmo que o seu. Eu escuto o soquete e coloco os dados em InputStreame, em seguida, dentro byte[]. Agora, quando estou tentando converter o byte[]em String (preciso usar o corpo da resposta para ataques), recebo personagens realmente engraçados cheios de aspas inteligentes e pontos de interrogação e sei lá o quê. Acredito que o seu problema seja o mesmo que o meu, pois ambos estamos lidando htmlem byte[]. Você pode aconselhar?
Parul S
A propósito, fui ao ponto de encontrar a codificação do meu sistema usando Sytem.properties e descobri que era "Cp1252". Agora, eu usei, String str=new String(buffer, "Cp1252");mas sem ajuda.
Parul S

Respostas:

141

A matriz de bytes contém caracteres em uma codificação especial (que você deve saber). A maneira de convertê-lo em String é:

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

By the way - os bytes brutos aparecem podem aparecer como decimais negativos apenas porque o tipo de dados java byteé assinado, ele cobre o intervalo de -128 a 127.


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

O valor (-109) é um caractere de controle não imprimível em UNICODE. Portanto, UTF-8 não é a codificação correta para esse fluxo de caracteres.

0x93em "Windows-1252" está a "citação inteligente" que você está procurando, portanto, o nome Java dessa codificação é "Cp1252". A próxima linha fornece um código de teste:

System.out.println(new String(new byte[]{-109}, "Cp1252")); 
Andreas Dolk
fonte
5
Tentei usar UTF-8 e ainda saiu como? 'S. Por que não está encontrando um mapeamento para esses valores negativos?
Josh
0x93 é um byte de continuação válido em UTF-8, embora - a presença desse byte apenas exclui que seja UTF-8 se não vier após um byte com os primeiros dois bits definidos.
Nick Johnson
1
@Josh Andreas explica o porquê - porque o bytetipo de dados Java é assinado. Os valores 'negativos' são apenas bytes com o conjunto de bytes mais significativo. Ele também explica qual é o conjunto de caracteres mais provável que você deve usar - Windows-1252. Você deve saber qual conjunto de caracteres usar de contexto ou convenção, entretanto, sem ter que adivinhar.
Nick Johnson
25

Java 7 e superior

Você também pode passar a codificação desejada para o Stringconstrutor como uma Charsetconstante de StandardCharsets . Isso pode ser mais seguro do que passar a codificação como um String, como sugerido nas outras respostas.

Por exemplo, para codificação UTF-8

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);
Davnicwil
fonte
1
Esta é uma repetição de uma resposta de 2011. -1
james.garriss
2
@ james.garriss Acho que não, já que estou apenas mencionando um novo construtor introduzido em java 7 que permite que a codificação seja passada como uma constante, o que na minha opinião é mais agradável e mais seguro do que a API anterior mencionado nas respostas anteriores em que a codificação foi passada como String, se for o caso.
davnicwil
11

Você pode tentar isso.

String s = new String(bytearray);
Muhammad Aamir Ali
fonte
9
Você pode tentar ... mas falhará em quase todos os 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();
    }
}

Resultado

65
65
A
Adi Sembiring
fonte
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
fonte
3
Este código vazará um recurso se readlançar uma exceção.
Raedwald,
4

Eu sugiro Arrays.toString(byte_array);

Depende do seu propósito. Por exemplo, eu queria salvar uma matriz de bytes exatamente como o formato que você pode ver no momento da depuração, que é algo assim: [1, 2, 3]Se você quiser salvar exatamente o mesmo valor sem converter os bytes para o formato de caractere, Arrays.toString (byte_array)faça isso. Mas se você quiser salvar caracteres em vez de bytes, você deve usar String s = new String(byte_array). Nesse caso, sé igual a equivalente a [1, 2, 3]em formato de caractere.

Questionador
fonte
Você pode dar mais informações sobre por que está sugerindo isso? (Isso resolverá o problema? Você pode dizer por que resolve?) Obrigado!
Dean J de
Depende do seu propósito. Por exemplo, eu queria salvar uma matriz de bytes exatamente como o formato que você pode ver no momento da depuração que é algo assim: [1, 2, 3] Se você deseja salvar exatamente o mesmo valor sem converter os bytes para o formato de caractere, Arrays.toString (byte_array) faz isso. Mas se você quiser salvar caracteres em vez de bytes, você deve usar String s = new String (byte_array). Nesse caso, s é igual a equivalente a [1, 2, 3] no formato de caractere.
Questionador de
@sas, você deve adicionar essas informações à sua própria resposta (editando-a) e não como um comentário. Geralmente no SO, você deve sempre ter em mente que os comentários podem ser excluídos a qualquer momento - as informações realmente importantes devem estar na própria resposta.
Jeen Broekstra
3

A resposta anterior de Andreas_D é boa. Vou apenas acrescentar que, onde quer que você esteja exibindo a saída, haverá uma fonte e uma codificação de caracteres e pode não suportar alguns caracteres.

Para descobrir se o problema é Java ou sua tela, faça o seguinte:

    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 terá mapeado todos os caracteres que não consegue entender para 0xfffd o caractere oficial de caracteres desconhecidos. Se você vir um '?' na saída, mas não está mapeado para 0xfffd, é sua fonte de exibição ou codificação que é o problema, não Java.

Simon G.
fonte