Je suis aux prises avec un étrange nom de fichier problème de codage lorsque la liste le contenu d'un répertoire dans la version 6 de Java sur OS X et Linux: l' File.listFiles()
et les méthodes connexes semblent revenir les noms de fichier dans un autre encodage que le reste du système.
Notez qu'il n'est pas simplement l'affichage de ces noms de fichiers que est me causer des problèmes. Je suis principalement intéressé à faire une comparaison des noms de fichier avec une télécommande système de stockage de fichiers, de sorte que je me soucie plus sur le contenu des chaînes de nom de l'encodage des caractères utilisé pour l'impression.
Voici un programme à démontrer. Il crée un fichier avec un nom Unicode puis imprime URL-encodé versions des noms de fichier obtenu à partir de la directement-créé le Fichier, et le fichier de même lorsqu'ils sont répertoriés sous un répertoire parent (vous devez exécuter ce code dans un répertoire vide). Les résultats montrent que le codage différent retourné par l' File.listFiles()
méthode.
String fileName = "Trîcky Nåme";
File file = new File(fileName);
file.createNewFile();
System.out.println("File name: " + URLEncoder.encode(file.getName(), "UTF-8"));
// Get parent (current) dir and list file contents
File parentDir = file.getAbsoluteFile().getParentFile();
File[] children = parentDir.listFiles();
for (File child: children) {
System.out.println("Listed name: " + URLEncoder.encode(child.getName(), "UTF-8"));
}
Voici ce que j'obtiens quand j'exécute ce code de test sur mes systèmes. Remarque l' %CC
contre %C3
caractère représentations.
OS X Snow Leopard:
File name: Tri%CC%82cky+Na%CC%8Ame
Listed name: Tr%C3%AEcky+N%C3%A5me
$ java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)
KUbuntu Linux (exécutant sur une machine virtuelle sur le même système OS X):
File name: Tri%CC%82cky+Na%CC%8Ame
Listed name: Tr%C3%AEcky+N%C3%A5me
$ java -version
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1)
OpenJDK Client VM (build 16.0-b13, mixed mode, sharing)
J'ai essayé différents hacks pour obtenir les chaînes d'accord, y compris le réglage de l' file.encoding
de la propriété du système et de divers LC_CTYPE
et LANG
variables d'environnement. Rien n'y fait, je ne veux recours à ces hacks.
Contrairement à ce (en quelque sorte liés?) question, je suis en mesure de lire les données à partir de la liste des fichiers en dépit de l'étrange noms
Je suis à court d'idées, et après de nombreuses heures de vaines de débogage et de recherches sur Google je suis prête pour certaines lumières.
Solution
Merci à Stephen P pour me mettre sur la bonne voie.
Le correctif d'abord, pour les impatients. Si vous compilez avec la version 6 de Java, vous pouvez utiliser le java.texte.Normalisateur de classe à normaliser les chaînes en une forme courante de votre choix, par ex.
// Normalize to "Normalization Form Canonical Decomposition" (NFD)
protected String normalizeUnicode(String str) {
Normalizer.Form form = Normalizer.Form.NFD;
if (!Normalizer.isNormalized(str, form)) {
return Normalizer.normalize(str, form);
}
return str;
}
Depuis java.text.Normalizer
n'est disponible que dans la version 6 de Java et plus tard, si vous avez besoin de compiler avec Java 5, vous pourriez avoir à recourir à l' sun.text.Normalizer
de la mise en œuvre et quelque chose comme cela en fonction de réflexion hack Voir aussi Comment cette fonction de normalisation de travail?
Cela seul suffit pour moi de décider, je ne prend pas en charge la compilation de mon projet avec Java 5 :|
Voici d'autres choses intéressantes que j'ai appris dans cette sordide aventure.
La confusion est causée par les noms de fichier en cours dans l'une des deux formes de normalisation qui ne peuvent pas être directement comparées: la Forme de Normalisation Canonique de Décomposition (NFD) ou de la Forme de Normalisation Canonique Composition (NFC). La première tend à avoir des lettres ASCII suivi par "modificateurs" pour ajouter des accents etc, tandis que le second a seulement les caractères étendus sans ACSCII personnage principal. Lire la page wiki de Stephen P références pour une meilleure explication.
-
Unicode littéraux de chaîne comme celle contenue dans le code d'exemple (et ceux reçus via HTTP, dans mon application réelle) sont dans la NFD forme, alors que les noms de fichiers retournés par l'
File.listFiles()
méthode de la NFC. La suite de la mini-exemple illustre les différences:String name = "Trîcky Nåme"; System.out.println("Original name: " + URLEncoder.encode(name, "UTF-8")); System.out.println("NFC Normalized name: " + URLEncoder.encode( Normalizer.normalize(name, Normalizer.Form.NFC), "UTF-8")); System.out.println("NFD Normalized name: " + URLEncoder.encode( Normalizer.normalize(name, Normalizer.Form.NFD), "UTF-8"));
Sortie:
Original name: Tri%CC%82cky+Na%CC%8Ame NFC Normalized name: Tr%C3%AEcky+N%C3%A5me NFD Normalized name: Tri%CC%82cky+Na%CC%8Ame
Si vous construisez un
File
objet avec un nom de chaîne, l'File.getName()
méthode retourne le nom quelle que soit la forme que vous lui avez donné à l'origine. Toutefois, si vous appelezFile
méthodes permettant de découvrir les noms sur leur propre, ils semblent revenir noms NFC forme. C'est potentiellement un méchant chat. Il a certainement gotchme.-
D'après la citation ci-dessous à partir de la documentation d'Apple noms de fichiers sont stockés dans décomposé (DNF) de la forme sur le système de fichiers HFS Plus:
Lorsque vous travaillez sous Mac OS, vous trouverez vous-même en utilisant un mélange de précomposé et décomposé Unicode. Par exemple, HFS Plus convertit tous les noms de fichiers à décomposée Unicode, tandis que les claviers Macintosh produisent généralement précomposé Unicode.
Si l'
File.listFiles()
méthode utilement (?) convertit les noms de fichiers à la (pré)composé (NFC) dans le formulaire.