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?

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.


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

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

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.


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

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.


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

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).


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).


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

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

0
Reader reader = new BufferedReader(
    new InputStreamReader(
        new ByteArrayInputStream(
            string.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));

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

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.