Comment convertir des chaînes vers et depuis des tableaux d'octets UTF8 en Java

239

En Java, j'ai une chaîne et je veux l'encoder comme un tableau d'octets (en UTF8, ou un autre encodage). Alternativement, j'ai un tableau d'octets (dans certains encodages connus) et je veux le convertir en une chaîne Java. Comment faire ces conversions?

mcherm
la source

Réponses:

355

Conversion de chaîne en octet []:

String s = "some text here";
byte[] b = s.getBytes(StandardCharsets.UTF_8);

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

byte[] b = {(byte) 99, (byte)97, (byte)116};
String s = new String(b, StandardCharsets.US_ASCII);

Vous devez, bien sûr, utiliser le nom de codage correct. Mes exemples utilisaient US-ASCII et UTF-8, les deux encodages les plus courants.

mcherm
la source
30
US-ASCII n'est actuellement pas un encodage très courant. Windows-1252 et ISO-8859-1 (qui sont des sur-ensembles d'ASCII) sont beaucoup plus répandus.
Michael Borgwardt,
11
En fait, je le trouve assez courant dans mon travail. Je lis souvent des flux d'octets qui ont peut-être été enregistrés sous Windows-1252 ou ISO-8859-1 ou même simplement comme "sortie de ce programme hérité que nous avons depuis 10 ans", mais qui contiennent des octets garantis valides Caractères US-ASCII. J'ai également souvent besoin de GÉNÉRER de tels fichiers (pour une consommation par code qui peut ou non être capable de gérer des caractères non ASCII. Fondamentalement, US-ASCII est le "plus grand dénominateur commun" de nombreux logiciels.
mcherm
1
Cependant, cette méthode ne signalera aucun problème lors de la conversion. C'est peut-être ce que vous voulez. Sinon, il est recommandé d'utiliser à la place CharsetEncoder.
Michael Piefel
7
@Pacerier car les documents pour le jeu de caractères répertorient "UTF-8" comme l'un des jeux de caractères standard. Je crois que votre orthographe est également acceptée, mais je suis allé avec ce que les doc ont dit.
mcherm
20
Depuis JDK7, vous pouvez utiliser StandardCharsets.UTF_8 docs.oracle.com/javase/7/docs/api/java/nio/charset/…
Rafael Membrives
95

Voici une solution qui évite d'effectuer la recherche Charset pour chaque conversion:

import java.nio.charset.Charset;

private final Charset UTF8_CHARSET = Charset.forName("UTF-8");

String decodeUTF8(byte[] bytes) {
    return new String(bytes, UTF8_CHARSET);
}

byte[] encodeUTF8(String string) {
    return string.getBytes(UTF8_CHARSET);
}
Mike Leonhard
la source
4
@mcherm: Même si la différence de performances est faible, je préfère utiliser des objets (Charset, URL, etc.) plutôt que leurs formes de chaîne lorsque cela est possible.
Bart van Heukelom
7
Remarque: chaîne publique "depuis 1.6" (octet [] octets, jeu de caractères Charset)
leo
1
En ce qui concerne "évite d'effectuer la recherche Charset pour chaque conversion" ... veuillez citer une source. Java.nio.charset.Charset n'est-il pas construit au-dessus de String.getBytes et a donc plus de surcharge que String.getBytes?
Pacerier
2
Les documents indiquent: "Le comportement de cette méthode lorsque cette chaîne ne peut pas être codée dans le jeu de caractères donné n'est pas spécifié. La classe CharsetEncoder doit être utilisée lorsqu'un contrôle plus important sur le processus de codage est requis."
paiego
24
Remarque: depuis Java 1.7, vous pouvez utiliser StandardCharsets.UTF_8pour un moyen constant d'accéder au jeu de caractères UTF-8.
Kat
17
String original = "hello world";
byte[] utf8Bytes = original.getBytes("UTF-8");
Jorge Ferreira
la source
Merci! Je l'ai récrit moi-même en ajoutant l'autre sens de conversion.
mcherm
1
@smink Le tiret n'est pas facultatif. Cela devrait utiliser "UTF-8"
Mel Nicholson
14

Vous pouvez convertir directement via le constructeur String (byte [], String) et la méthode getBytes (String). Java expose les jeux de caractères disponibles via la classe Charset . La documentation JDK répertorie les encodages pris en charge .

Dans 90% des cas, ces conversions sont effectuées sur des flux, vous devez donc utiliser les classes Reader / Writer . Vous ne décoderiez pas de manière incrémentielle en utilisant les méthodes String sur des flux d'octets arbitraires - vous vous laisseriez ouvert aux bogues impliquant des caractères multi-octets.

McDowell
la source
Peux-tu élaborer? Si mon application encode et décode des chaînes UTF-8, quelle est la préoccupation concernant les caractères multi-octets?
raffian du
@raffian Des problèmes peuvent survenir si vous ne transformez pas toutes les données de personnage en une seule fois. Voir ici pour un exemple.
McDowell du
12

Mon implémentation tomcat7 accepte les chaînes comme ISO-8859-1; malgré le type de contenu de la requête HTTP. La solution suivante a fonctionné pour moi lorsque j'essayais d'interpréter correctement des caractères comme «é».

byte[] b1 = szP1.getBytes("ISO-8859-1");
System.out.println(b1.toString());

String szUT8 = new String(b1, "UTF-8");
System.out.println(szUT8);

Lorsque vous tentez d'interpréter la chaîne comme US-ASCII, les informations sur les octets n'étaient pas correctement interprétées.

b1 = szP1.getBytes("US-ASCII");
System.out.println(b1.toString());
paiego
la source
8
Pour info, à partir de Java 7, vous pouvez utiliser des constantes pour les noms de jeux de caractères tels que StandardCharSets.UTF_8et StandardCharSets.ISO_8859_1.
Basil Bourque
J'ai sauvé ma journée, fonctionnant parfaitement pour la première solution mentionnée ci-dessus.
Hassan Jamil
7

Comme alternative, StringUtils d'Apache Commons peut être utilisé.

 byte[] bytes = {(byte) 1};
 String convertedString = StringUtils.newStringUtf8(bytes);

ou

 String myString = "example";
 byte[] convertedBytes = StringUtils.getBytesUtf8(myString);

Si vous avez un jeu de caractères non standard, vous pouvez utiliser getBytesUnchecked () ou newString () en conséquence.

vtor
la source
4
Notez que ce StringUtils de Commons Codec , pas Commons Lang.
Arend c. Reinersdorff
Oui, un peu fou! Pour les utilisateurs de Gradle, Maven: "commons-codec: commons-codec: 1.10" (au moment de la rédaction). Cela est également fourni en tant que dépendance avec Apache POI, par exemple. A part ça Apache Commons à la rescousse, comme toujours!
mike rodent
2

Pour décoder une série d'octets en un message de chaîne normale, j'ai finalement réussi à le faire fonctionner avec le codage UTF-8 avec ce code:

/* Convert a list of UTF-8 numbers to a normal String
 * Usefull for decoding a jms message that is delivered as a sequence of bytes instead of plain text
 */
public String convertUtf8NumbersToString(String[] numbers){
    int length = numbers.length;
    byte[] data = new byte[length];

    for(int i = 0; i< length; i++){
        data[i] = Byte.parseByte(numbers[i]);
    }
    return new String(data, Charset.forName("UTF-8"));
}
Bouke Woudstra
la source
1

Si vous utilisez ASCII 7 bits ou ISO-8859-1 (un format incroyablement commun), vous n'avez pas du tout besoin de créer un nouveau java.lang.String . Il est beaucoup plus performant de simplement convertir l'octet en caractère:

Exemple de travail complet:

for (byte b : new byte[] { 43, 45, (byte) 215, (byte) 247 }) {
    char c = (char) b;
    System.out.print(c);
}

Si vous n'utilisez pas de caractères étendus comme Ä, Æ, Å, Ç, Ï, Ê et que vous pouvez être sûr que les seules valeurs transmises sont des 128 premiers caractères Unicode, alors ce code fonctionnera également pour UTF-8 et ASCII étendu. (comme cp-1252).

Pacerier
la source
1

Je ne peux pas commenter mais je ne veux pas commencer un nouveau fil. Mais cela ne fonctionne pas. Un simple aller-retour:

byte[] b = new byte[]{ 0, 0, 0, -127 };  // 0x00000081
String s = new String(b,StandardCharsets.UTF_8); // UTF8 = 0x0000, 0x0000,  0x0000, 0xfffd
b = s.getBytes(StandardCharsets.UTF_8); // [0, 0, 0, -17, -65, -67] 0x000000efbfbd != 0x00000081

J'aurais besoin de b [] le même tableau avant et après l'encodage, ce qui n'est pas le cas (cela renvoie à la première réponse).

jschober
la source
0
//query is your json   

 DefaultHttpClient httpClient = new DefaultHttpClient();
 HttpPost postRequest = new HttpPost("http://my.site/test/v1/product/search?qy=");

 StringEntity input = new StringEntity(query, "UTF-8");
 input.setContentType("application/json");
 postRequest.setEntity(input);   
 HttpResponse response=response = httpClient.execute(postRequest);
Ran Adler
la source
Est-ce que String Entity convertit la «requête» en utf-8 ou se souvient-il simplement de la connexion de l'entité?
SyntaxRules
0
Charset UTF8_CHARSET = Charset.forName("UTF-8");
String strISO = "{\"name\":\"א\"}";
System.out.println(strISO);
byte[] b = strISO.getBytes();
for (byte c: b) {
    System.out.print("[" + c + "]");
}
String str = new String(b, UTF8_CHARSET);
System.out.println(str);
Nitish Raj Srivastava
la source
0
Reader reader = new BufferedReader(
    new InputStreamReader(
        new ByteArrayInputStream(
            string.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Макс Даниленко
la source
-9

terriblement en retard mais je viens de rencontrer ce problème et voici ma solution:

private static String removeNonUtf8CompliantCharacters( final String inString ) {
    if (null == inString ) return null;
    byte[] byteArr = inString.getBytes();
    for ( int i=0; i < byteArr.length; i++ ) {
        byte ch= byteArr[i]; 
        // remove any characters outside the valid UTF-8 range as well as all control characters
        // except tabs and new lines
        if ( !( (ch > 31 && ch < 253 ) || ch == '\t' || ch == '\n' || ch == '\r') ) {
            byteArr[i]=' ';
        }
    }
    return new String( byteArr );
}
savio
la source
2
Tout d'abord, ce n'est pas une conversion: c'est la suppression d'octets non imprimables. Deuxièmement, il suppose que l'encodage par défaut du système d'exploitation sous-jacent est vraiment basé sur ASCII pour les caractères imprimables (ne fonctionnera pas sur les mainframes IBM utilisant EBCDIC, par exemple).
Isaac