Comment convertir un tableau d'octets en chaîne et vice versa?

248

Je dois convertir un tableau d'octets en chaîne dans Android, mais mon tableau d'octets contient des valeurs négatives.

Si je convertis à nouveau cette chaîne en tableau d'octets, les valeurs que j'obtiens sont différentes des valeurs du tableau d'octets d'origine.

Que puis-je faire pour obtenir une conversion appropriée? Le code que j'utilise pour effectuer la conversion est le suivant:

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

Je suis coincé dans ce problème.

Jyotsna
la source
3
Pourquoi essayez-vous de convertir des données binaires arbitraires en une chaîne en premier lieu? Mis à part tous les problèmes de charset que les réponses mentionnent déjà, il y a aussi le fait que vous abusez de String si vous faites cela. Quel est le problème avec l'utilisation d'un byte[]pour vos données binaires et Stringpour votre texte?
Joachim Sauer
8
@Joachim - parfois vous avez des outils externes qui peuvent faire des choses comme stocker des chaînes. Dans ce cas, vous voulez pouvoir transformer un tableau d'octets en une chaîne (encodée d'une manière ou d'une autre).
James Moore

Réponses:

377

Votre tableau d'octets doit avoir un certain encodage. Le codage ne peut pas être ASCII si vous avez des valeurs négatives. Une fois que vous avez compris cela, vous pouvez convertir un ensemble d'octets en une chaîne en utilisant:

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

Il y a un tas de codages que vous pouvez utiliser, regardez la classe charset dans les javadocs Sun .

omerkudat
la source
4
@MauricePerry pouvez-vous expliquer pourquoi cela ne fonctionnera pas UTF-8?
Asif Mushtaq
12
@UnKnown car UTF-8 code certains caractères sous forme de chaînes de 2 ou 3 octets. Tous les tableaux d'octets ne sont pas une chaîne codée UTF-8 valide. ISO-8859-1 serait un meilleur choix: ici, chaque caractère est codé comme un octet.
Maurice Perry
1
Cela peut fonctionner, mais vous devez éviter d'utiliser le constructeur de chaînes à tout prix.
hfontanez
pour mapper un octet à un caractère (avec 8859-1) et aucune gestion des exceptions (avec nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
iman
1
depuis Java 1.7, vous pouvez utiliser une nouvelle chaîne (octets, StandardCharsets.UTF_8)
ihebiheb
101

La "conversion appropriée" entre byte[]et Stringconsiste à indiquer explicitement l'encodage que vous souhaitez utiliser. Si vous commencez par un byte[]et qu'il ne contient en fait pas de données texte, il n'y a pas de "conversion appropriée". Strings sont pour le texte, byte[]pour les données binaires, et la seule chose vraiment sensée à faire est d' éviter de convertir entre eux à moins que vous ne deviez absolument le faire.

Si vous devez vraiment utiliser un Stringpour contenir des données binaires, le moyen le plus sûr est d'utiliser le codage Base64 .

Michael Borgwardt
la source
1
Oui, le codage de caractères est quelque chose que vous devez savoir pour convertir entre chaînes et octets.
Raedwald
4
Base64 et vous m'avez sauvé la vie
mstzn
2
L'encodage Base64 a résolu mon problème. UTF-8 ne fonctionnait pas pour toutes les entrées
Al-Alamin
37

Le problème racine est (je pense) que vous utilisez involontairement un jeu de caractères pour lequel:

 bytes != encode(decode(bytes))

dans certains cas. UTF-8 est un exemple d'un tel jeu de caractères. Plus précisément, certaines séquences d'octets ne sont pas des codages valides en UTF-8. Si le décodeur UTF-8 rencontre l'une de ces séquences, il est susceptible de supprimer les octets incriminés ou de les décoder en tant que point de code Unicode pour "aucun caractère de ce type". Naturellement, lorsque vous essayez ensuite de coder les caractères en octets, le résultat sera différent.

La solution est:

  1. Soyez explicite sur le codage de caractères que vous utilisez; c'est-à-dire utiliser un constructeur String et une String.toByteArrayméthode avec un jeu de caractères explicite.
  2. Utilisez le bon jeu de caractères pour vos données d'octets ... ou alternativement un (tel que "Latin-1" où toutes les séquences d'octets sont mappées à des caractères Unicode valides.
  3. Si vos octets sont (vraiment) des données binaires et que vous souhaitez pouvoir les transmettre / recevoir sur un canal "texte", utilisez quelque chose comme le codage Base64 ... qui est conçu à cet effet .
Stephen C
la source
1
Merci pour le conseil d'utilisation de l'encodage "Latin-1"!
Gonzo
31

Nous avons juste besoin d'en construire un nouveau Stringavec le tableau: http://www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

Les octets de la chaîne résultante diffèrent selon le jeu de caractères que vous utilisez. nouvelle chaîne (octets) et nouvelle chaîne (octets, Charset.forName ("utf-8")) et nouvelle chaîne (octets, Charset.forName ("utf-16")) auront tous des tableaux d'octets différents lorsque vous appelez String # getBytes () (selon le jeu de caractères par défaut)

Ravindranath Akila
la source
9
Non. Les octets de la chaîne résultante diffèrent selon le jeu de caractères que vous utilisez. new String(bytes)et new String(bytes, Charset.forName("utf-8"))et new String(bytes, Charset.forName("utf-16"))auront tous des tableaux d'octets différents lorsque vous appelez String#getBytes()(selon le jeu de caractères par défaut)
NS du Toit
1
Trompeur. Le chars (et donc le texte affiché) du résultat Stringdiffère lors du décodage bytesdifféremment. La conversion en octets en utilisant le codage par défaut (utilisez String#getBytes("charset")pour spécifier le contraire) sera nécessairement différente car elle convertit des entrées différentes. Les chaînes ne stockent pas les éléments dont byte[]elles sont issues, elles charn'ont pas d'encodage et Stringne les stockent pas autrement.
zapl
14

Utiliser new String(byOriginal)et reconvertir en byte[]utilisant getBytes()ne garantit pas deux byte[]avec des valeurs égales. Cela est dû à un appel vers StringCoding.encode(..)lequel va coder le Stringto Charset.defaultCharset(). Pendant cet encodage, l'encodeur peut choisir de remplacer les caractères inconnus et d'effectuer d'autres modifications. Par conséquent, l'utilisation String.getBytes()peut ne pas retourner un tableau égal à celui que vous avez initialement transmis au constructeur.

sfussenegger
la source
9

Pourquoi était le problème: Comme quelqu'un l'a déjà spécifié: Si vous commencez par un octet [] et qu'il ne contient en fait pas de données texte, il n'y a pas de "conversion appropriée". Les chaînes sont pour le texte, l'octet [] est pour les données binaires, et la seule chose vraiment sensée à faire est d'éviter de convertir entre elles à moins que vous ne deviez absolument le faire.

J'observais ce problème lorsque j'essayais de créer l'octet [] à partir d'un fichier pdf, puis de le convertir en chaîne, puis de prendre la chaîne en entrée et de la reconvertir en fichier.

Assurez-vous donc que votre logique d'encodage et de décodage est la même que moi. J'ai explicitement codé l'octet [] en Base64 et l'ai décodé pour recréer le fichier.

Cas d' utilisation: En raison de certaines limitations je tentais à envoyer byte[]dans request(POST)et le processus se présente comme suit:

Fichier PDF >> Base64.encodeBase64 (octet []) >> Chaîne >> Envoyer une demande (POST) >> recevoir une chaîne >> Base64.decodeBase64 (octet []) >> créer un binaire

Essayez ceci et cela a fonctionné pour moi ..

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
la source
6

Cela fonctionne bien pour moi:

String cd="Holding some value";

Conversion d'une chaîne en octet []:

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

Conversion de l'octet [] en chaîne:

cd = new sun.misc.BASE64Encoder().encode(cookie);
LED
la source
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
la source
1
Cela ne répond pas à la question.
james.garriss
Ne répond pas à la question mais a été utile +1
Lazy Ninja
5

J'ai remarqué quelque chose qui ne figure dans aucune des réponses. Vous pouvez convertir chacun des octets du tableau d'octets en caractères et les placer dans un tableau de caractères. Ensuite, la chaîne est

new String(cbuf)
où cbuf est le tableau char. Pour reconvertir, parcourez la chaîne en convertissant chacun des caractères en octets à placer dans un tableau d'octets, et ce tableau d'octets sera le même que le premier.


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
la source
2

javax.xml.bind.DatatypeConverter devrait le faire:

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

Voici quelques méthodes qui convertissent un tableau d'octets en chaîne. Je les ai testés, ils fonctionnent 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();

}
user2288580
la source
2

Même si

new String(bytes, "UTF-8")

est correct, il lance un UnsupportedEncodingExceptionqui vous oblige à faire face à une exception vérifiée. Vous pouvez utiliser comme alternative un autre constructeur depuis Java 1.6 pour convertir un tableau d'octets en String:

new String(bytes, StandardCharsets.UTF_8)

Celui-ci ne lève aucune exception.

La reconversion doit également être effectuée avec StandardCharsets.UTF_8:

"test".getBytes(StandardCharsets.UTF_8)

Encore une fois, vous évitez d'avoir à traiter les exceptions vérifiées.

gil.fernandes
la source
1

J'ai réussi à convertir le tableau d'octets en chaîne avec cette méthode:

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
la source
1

Bien que l'encodage base64 soit sûr et que l'on puisse dire «la bonne réponse», je suis arrivé ici à la recherche d'un moyen de convertir un tableau d'octets Java vers / à partir d'une chaîne Java telle quelle. Autrement dit, où chaque membre du tableau d'octets reste intact dans son homologue String, sans espace supplémentaire requis pour l'encodage / transport.

Cette réponse décrivant des encodages transparents 8 bits m'a été très utile. J'ai utilisé ISO-8859-1sur des téraoctets de données binaires pour convertir avec succès (chaîne binaire <->) sans les exigences d'espace gonflées nécessaires pour un encodage base64, donc est sans danger pour mon cas d'utilisation - YMMV.

Cela a également été utile pour expliquer quand / si vous devriez expérimenter.

Reed Sandberg
la source
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
la source
Pourquoi? Pourquoi passer par Base64 pour convertir un octet en une chaîne? Les frais généraux.
james.garriss
0

Voici le code de travail.

            // 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
la source
-1

Essayez de spécifier un jeu de caractères 8 bits dans les deux conversions. ISO-8859-1 par exemple.

Maurice Perry
la source
-1

Lisez les octets de l' Stringutilisation ByteArrayInputStreamet encapsulez-le avec BufferedReaderlequel est Char Stream au lieu de Byte Stream qui convertit les données d'octets en chaîne.

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

    }
}

La sortie est:

Sajal est un bon garçon

Sajal Goyal
la source
-1

Vous pouvez utiliser simple for loop pour la conversion:

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

Une chaîne est une collection de caractères (16 bits non signés). Donc, si vous allez convertir des nombres négatifs en une chaîne, ils seront perdus lors de la traduction.

Crapaud
la source
1
-1: C'est incorrect. Bien que 'byte' soit un type signé en Java, ils sont traités comme non signés par le code de la bibliothèque qui effectue l'encodage et le décodage des jeux de caractères.
Stephen C
Un bel exemple pourquoi avoir un type de données 8 bits non signé est vraiment une bonne idée d'avoir dans une langue. Évite toute confusion inutile; ^)
Toad
Soyez prudent en supposant qu'un caractère Java sera de 16 bits, en raison de l'UTF-16 de Java, ils peuvent s'étendre jusqu'à 32 bits
Joe Plante
1
@Toad en fait oui, certains caractères Unicode stockés en UTF-16 occupent deux points de code, soit 32 bits. La même chose se produit en UTF-8: certains caractères utilisent deux / trois / quatre points de code, c'est-à-dire 16/24/32 bits. En fait, c'est exactement de cela qu'il s'agit (UTF! = Unicode).
CAFxX
1
@Toad, vous obtiendrez le premier substitut, c'est-à-dire uniquement la première "moitié" du personnage. Regardez les documents de la méthode String.charAt et de la 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
la source
Passez l'encodage Charset comme argument à getBytes
Shyam Sreenivasan
1
Vous pouvez envisager d'étoffer cette réponse avec une explication en plus du code.
Charlie Schliesser