(редактирующий для разъяснения и добавляющий некоторый код)
Привет, у Нас есть требование для парсинга данных, отправленных от пользователей во всем мире. Наши системы Linux имеют локаль по умолчанию en_US.UTF-8. Однако мы часто получаем файлы с диакритическими знаками на их имена такой как"special_á_ã_è_characters.doc
". Хотя ОС может иметь дело с этими прекрасными файлами, и strace показывает ОС, передающую корректное имя файла программе Java, Java munges имена, и бросает "файл, не найденный" io исключение, пытающееся открыть их.
Эта простая программа может проиллюстрировать проблему:
import java.io.*;
import java.text.*;
public class load_i18n
{
public static void main( String [] args ) {
File actual = new File(".");
for( File f : actual.listFiles()){
System.out.println( f.getName() );
}
}
}
Запущение этой программы в каталоге, содержащем файл special_á_ã_è_characters.doc
и американская английская локаль по умолчанию дает:
special_�_�_�_characters.doc
Установка языка через экспорт LANG=es_ES@UTF-8 распечатывает имя файла правильно (но недопустимое решение, так как вся система теперь работает на испанском языке.) Установка Explicitly Локаль в программе как следующее не имеет никакого эффекта также. Ниже я изменил программу для a) попытаться открыть файл и b) распечатывания имени и в ASCII и как массив байтов, когда этому не удается открыть файл:
import java.io.*;
import java.util.Locale;
import java.text.*;
public class load_i18n
{
public static void main( String [] args ) {
// Stream to read file
FileInputStream fin;
Locale locale = new Locale("es", "ES");
Locale.setDefault(locale);
File actual = new File(".");
System.out.println(Locale.getDefault());
for( File f : actual.listFiles()){
try {
fin = new FileInputStream (f.getName());
}
catch (IOException e){
System.err.println ("Can't open the file " + f.getName() + ". Printing as byte array.");
byte[] textArray = f.getName().getBytes();
for(byte b: textArray){
System.err.print(b + " ");
}
System.err.println();
System.exit(-1);
}
System.out.println( f.getName() );
}
}
}
Это производит вывод
es_ES
load_i18n.class
Can't open the file special_�_�_�_characters.doc. Printing as byte array.
115 112 101 99 105 97 108 95 -17 -65 -67 95 -17 -65 -67 95 -17 -65 -67 95 99 104 97 114 97 99 116 101 114 115 46 100 111 99
Это показывает, что проблемой НЕ является просто проблема с консольным дисплеем как те же символы, и их представления производятся в формате ASCII или байте. На самом деле консольный дисплей действительно работает даже когда с помощью LANG=en_US.UTF-8 для некоторых утилит как эхо удара:
[mjuric@arrhchadm30 tmp]$ echo $LANG
en_US.UTF-8
[mjuric@arrhchadm30 tmp]$ echo *
load_i18n.class special_á_ã_è_characters.doc
[mjuric@arrhchadm30 tmp]$ ls
load_i18n.class special_?_?_?_characters.doc
[mjuric@arrhchadm30 tmp]$
Действительно ли возможно изменить этот код таким способом, которым, когда выполнено в соответствии с Linux с LANG=en_US.UTF-8, это читает имя файла таким способом, которым это может быть успешно открыто?
Во-первых, используемая кодировка символов не связана напрямую с локалью. Так что смена локали не сильно поможет.
Во-вторых, �
типично для символа замены Unicode U + FFFD �
, печатаемого в ISO-8859-1 вместо UTF-8. Вот свидетельство:
System.out.println(new String("�".getBytes("UTF-8"), "ISO-8859-1")); // �
Итак, есть две проблемы:
�
. Для Sun JVM аргумент VM -Dfile.encoding = UTF-8
должен исправить первую проблему. Вторую проблему нужно исправить в настройках консоли. Если вы используете, например, Eclipse, вы можете изменить его в Window> Preferences> General> Workspace> Text File Encoding . Установите также UTF-8.
Обновление : Согласно вашему обновлению:
byte[] textArray = f.getName().getBytes();
Это должно было быть следующее, чтобы исключить влияние кодировки платформы по умолчанию:
byte[] textArray = f.getName().getBytes("UTF-8");
Если это все еще отображается так же, значит проблема глубже. Какую именно JVM вы используете? Сделайте java -версию
. Как было сказано ранее, аргумент -Dfile.encoding
специфичен для Sun JVM. Некоторые машины Linux поставляются с GNU JVM или OpenJDK JVM, и тогда этот аргумент может не работать.
Системное свойство Java file.encoding
должно соответствовать кодировке символов консоли. Это свойство должно быть установлено при запуске java
в командной строке:
java -Dfile.encoding=UTF-8 …
Обычно это происходит автоматически, потому что кодировка консоли обычно является кодировкой платформы по умолчанию, и Java будет использовать кодировку платформы по умолчанию, если вы не Не указываю одно явно.