Conversion d'un tableau d'octets en chaîne (Java)

85

J'écris une application Web dans Google App Engine. Il permet aux gens de modifier essentiellement le code html qui est stocké sous forme de .htmlfichier dans le blobstore.

J'utilise fetchData pour renvoyer un byte[]de tous les caractères du fichier. J'essaye d'imprimer dans un html pour que l'utilisateur modifie le code html. Tout fonctionne très bien!

Voici mon seul problème maintenant:

Le tableau d'octets rencontre des problèmes lors de la conversion en chaîne. Des citations intelligentes et quelques personnages ont l'air géniaux. (symboles? ou japonais, etc.) Plus précisément, ce sont plusieurs octets que je vois qui ont des valeurs négatives qui causent le problème.

Les guillemets intelligents reviennent au fur -108et à mesure -109dans le tableau d'octets. Pourquoi cela et comment puis-je décoder les octets négatifs pour afficher le codage de caractères correct?

Josh
la source
Duplicata de stackoverflow.com/questions/1536054/…
james.garriss
Salut, je sais que c'est un très vieux poste mais je suis confronté à des problèmes similaires. Je crée un proxy man-in-the-middle pour ssl. Le problème auquel je suis confronté est le même que le vôtre. J'écoute la prise et j'introduis les données InputStream, puis j'y vais byte[]. Maintenant, quand j'essaie de convertir le byte[]en String (je dois utiliser le corps de réponse pour les attaques), j'obtiens des personnages vraiment amusants pleins de guillemets et de points d'interrogation intelligents et autres. Je crois problème vôtre est le même que le mien que nous avons tous deux traitons htmldans byte[]. Pouvez-vous s'il vous plaît des conseils?
Parul S
Au fait, je suis allé jusqu'à trouver l'encodage de mon système à l'aide de Sytem.properties et je l'ai trouvé "Cp1252". Maintenant, j'ai utilisé String str=new String(buffer, "Cp1252");mais aucune aide.
Parul S

Réponses:

141

Le tableau d'octets contient des caractères dans un encodage spécial (que vous devez savoir). La façon de le convertir en chaîne est:

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

Au fait - les octets bruts peuvent apparaître sous forme de décimales négatives simplement parce que le type de données java byteest signé, il couvre la plage de -128 à 127.


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

La valeur (-109) est un caractère de contrôle non imprimable dans UNICODE. Donc UTF-8 n'est pas le bon encodage pour ce flux de caractères.

0x93dans "Windows-1252" est le "guillemet intelligent" que vous recherchez, donc le nom Java de cet encodage est "Cp1252". La ligne suivante fournit un code de test:

System.out.println(new String(new byte[]{-109}, "Cp1252")); 
Andreas Dolk
la source
5
J'ai essayé d'utiliser UTF-8 et il est toujours sorti comme?. Comment se fait-il qu'il ne trouve pas de correspondance pour ces valeurs négatives?
Josh
0x93 est un octet de continuation valide en UTF-8, cependant - la présence de cet octet exclut seulement qu'il soit UTF-8 s'il ne vient pas après un octet avec les deux premiers bits définis.
Nick Johnson
1
@Josh Andreas explique pourquoi - parce que le bytetype de données de Java est signé. Les valeurs «négatives» ne sont que des octets avec le jeu d'octets le plus significatif. Il explique également quel est le jeu de caractères le plus probable que vous devriez utiliser - Windows-1252. Vous devez savoir quel jeu de caractères utiliser à partir du contexte ou de la convention, sans avoir à deviner.
Nick Johnson
25

Java 7 et supérieur

Vous pouvez également transmettre l'encodage souhaité au Stringconstructeur en tant que Charsetconstante à partir de StandardCharsets . Cela peut être plus sûr que de passer l'encodage en tant queString , comme suggéré dans les autres réponses.

Par exemple, pour le codage UTF-8

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);
Davnicwil
la source
1
Ceci est une répétition d'une réponse de 2011. -1
james.garriss
2
@ james.garriss Je ne pense pas que ce soit le cas, dans la mesure où je mentionne juste un nouveau constructeur introduit dans java 7 permettant à l'encodage d'être passé comme une constante, ce qui à mon avis est plus agréable et plus sûr que l'API précédente mentionné dans les réponses précédentes où le codage a été passé en tant que chaîne, voire pas du tout.
davnicwil
11

Vous pouvez essayer ceci.

String s = new String(bytearray);
Muhammad Aamir Ali
la source
9
Vous pouvez essayer ... mais cela échouera dans presque tous les cas.
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();
    }
}

Production

65
65
A
Adi Sembiring
la source
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
la source
3
Ce code perdra une ressource si le readlève une exception.
Raedwald
4

je suggère Arrays.toString(byte_array);

Cela dépend de votre objectif. Par exemple, je voulais enregistrer un tableau d'octets exactement comme le format que vous pouvez voir au moment du débogage qui est quelque chose comme ceci: [1, 2, 3]Si vous voulez enregistrer exactement la même valeur sans convertir les octets au format de caractère, faites Arrays.toString (byte_array)ceci. Mais si vous souhaitez enregistrer des caractères au lieu d'octets, vous devez utiliser String s = new String(byte_array). Dans ce cas, sest égal à l'équivalent du [1, 2, 3]format du caractère.

Questionneur
la source
Pouvez-vous expliquer pourquoi vous suggérez cela? (Cela résoudra-t-il le problème? Pouvez-vous dire pourquoi cela le résout?) Merci!
Dean J
Cela dépend de votre objectif. Par exemple, je voulais enregistrer un tableau d'octets exactement comme le format que vous pouvez voir au moment du débogage qui est quelque chose comme ceci: [1, 2, 3] Si vous voulez enregistrer exactement la même valeur sans convertir les octets au format de caractères, Arrays.toString (byte_array) fait cela ,. Mais si vous souhaitez enregistrer des caractères au lieu d'octets, vous devez utiliser String s = new String (byte_array). Dans ce cas, s est égal à l'équivalent de [1, 2, 3] au format caractère.
Interlocuteur le
@sas, vous devez ajouter ces informations à votre réponse elle-même (en la modifiant) plutôt qu'en tant que commentaire. En règle générale, vous devez toujours garder à l'esprit que les commentaires peuvent à tout moment être supprimés - les informations vraiment importantes doivent se trouver dans la réponse elle-même.
Jeen Broekstra
3

La réponse précédente d'Andreas_D est bonne. Je vais juste ajouter que partout où vous affichez la sortie, il y aura une police et un encodage de caractères et il se peut que certains caractères ne soient pas pris en charge.

Pour déterminer si c'est Java ou votre affichage qui pose problème, procédez comme suit:

    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 aura mappé tous les caractères qu'il ne peut pas comprendre à 0xfffd le caractère officiel des caractères inconnus. Si vous voyez un '?' dans la sortie, mais il n'est pas mappé à 0xfffd, c'est votre police d'affichage ou votre encodage qui est le problème, pas Java.

Simon G.
la source