JEP 400, UTF-8 par défaut

Contexte

L’encodage par défaut était calculé au démarrage en fonction du système d’exploitation, les paramètres régionaux de l’utilisateur ou d’autres facteurs.

En effet, qui n’a jamais eu ce type de caractère à l’écran ou dans ces fichiers ? :

UTF-8 par défaut

La solution généralement était de préciser la propriété -Dfile.encoding=…​. Cela permet de fixer l’encodage par défaut.

L’objectif est de faire en sorte que UTF-8 soit l’encodage par défaut quelque soit le système. Cela permet de simplifier, d’améliorer la portabilité et notamment la cohérence de la plateforme.

Le choix

Le choix du jeu de caractère UTF-8 fut logique. C’est encodage le plus utilisé sur le web (97,7% à ce jour). C’est aussi un encodage standard au niveau de XML et JSON. Java l’utilise déjà par défaut pour l’API NIO.

A partir du JDK 18, la JVM prendra l’encodage UTF-8 par défaut. Nous pouvons le vérifier avec jshell

jshell> java.nio.charset.Charset.defaultCharset()
$4 ==> UTF-8

astuce Pensez à importer le package java.nio.charset afin de pouvoir manipuler directement la classe Charset.

Pour bien comprendre, nous configurons notre système pour utiliser l’encodage ISO-8859-15. Pour vérifier, nous utilisons la commande locale.

LANG=fr_FR@euro
...

Ainsi, avec un JDK 17, nous obtenons

jshell> Charset.defaultCharset()
$4 ==> ISO-8859-15

Alors qu’avec un JDK 18, nous obtenons toujours UTF-8

jshell> Charset.defaultCharset()
$4 ==> UTF-8

Si nous lançons un JDK 17 en précisant la propriété file.encoding="UTF-8", nous obtenons bien l’encodage UTF-8 bien que notre système est en ISO-8859-15.

jshell> Charset.defaultCharset()
$4 ==> UTF-8

Passage de propriété pour jshell

Pour les tests, nous sommes amenés à devoir modifier les propriétés de la JVM.

Pour passer une propriété avec jshell, il faut utiliser l’option -R. Nous obtenons la ligne de commande suivante :

/../jdk-xy/bin/jshell -R-Dfile.encoding="UTF-8"

Préparation

Cela était en préparation depuis quelques temps.

Nouvelle propriété

Le JDK 17 a introduit une nouvelle propriété native.encoding. Cette propriété permet de connaitre l’encodage du système hôte. Nous comprenons d’autant plus l’intérêt de cette propriété maintenant.

Au delà de cette propriété, il est à noter que les consoles standard ou d’erreurs restent avec l’encodage natif.

Et le JDK 18 apporte une nouvelle méthode charset() sur la classe PrintStream pour connaitre directement l’encodage utilisé.

printstream charset

Cela permet d’en tenir compte au niveau des flux.

jshell> System.out.charset()
$5 ==> ISO-8859-15
jshell> System.err.charset();
$6 ==> ISO-8859-15

Fichier de propriétés

Pour rappel, c’est seulement à partir du JDK 9 que les fichiers de propriétés sont au format UTF-8 via la JEP 226: UTF-8 Property Resource Bundles

Cohérence de la plateforme

Je vous parlais de cohérence de la plateforme. Il faut effectivement savoir que les API n’étaient pas uniformes pour les valeurs par défaut.

Prenons l’exemple de la classe java.io.FileWriter avec deux constructeurs :

  • FileWriter(File file)

  • FileWriter(File file, Charset charset)

Le premier constructeur dépend du jeu de caractère par défaut Charset.defaultCharset(). Donc ce dernier dépendait du système et de la configuration du système.

En revanche, si je prends la classe java.nio.Files avec les méthodes lines :

  • Files.lines(Path path)

  • Files.lines(Path path, Charset charset)

Cette fois, la méthode lit le fichier avec l’encodage UTF-8. Cela est le cas pour l’ensemble des méthodes de la classe Files et de l’api NIO.

Maintenant, c’est plus simple, toutes les API sont en UTF-8 par défaut.

Compatibilité

La compatibilité reste un point fort de Java et une des préoccupations majeures lors des changements. L’équipe a prévu de garder la compatibilité avec l’ancien mode de calcul de l’encodage. Pour cela, il faut mettre à la valeur COMPAT à la propriété file.encoding

Voici le résultat sur un système Linux où la locale est toujours à ISO-8859-15.

/../jdk-xy/bin/jshell -R-Dfile.encoding="COMPAT"
  Welcome to JShell -- Version 18-ea
|  For an introduction type: /help intro

jshell> System.getProperty("file.encoding")
$1 ==> "ISO-8859-15"

Vérification

Si vous êtes concerné par le changement d’encodage, vous pouvez tester simplement l’impact en utilisant la propriété file.encoding=UTF-8. Cela donne la commande suivante

/../jdk-[8-17]/bin/java -Dfile.encoding=UTF-8 ....

astuce Il est à noter que vous pouvez le faire tout de suite avec votre JDK.

Compilation

Parlons de compilation, mais pourquoi ?

Tout simplement parce que le compilateur Javac est aussi concerné. Ce dernier va lire et écrire les fichiers en UTF-8.

De la même manière, vous pouvez vous assurer d’ores et déjà du bon fonctionnement en précisant la propriété file.encoding.

/../jdk-[8-17]/bin/javac -Dfile.encoding=UTF-8 ....

Bien que je ne le conseille pas. Puisque les sources sont en UTF-8, il est possible d’utiliser des accents si nous le souhaitons.

class Hello {

   public static void main(String[] args) {
      String téléphone = "01.02.03.04.05";
      System.out.println("Tél : " + téléphone);
   }
}