Как я могу открыть файлы, содержащие акценты в Java? - PullRequest
12 голосов
/ 18 июня 2010

( редактирование для уточнения и добавление некоторого кода )

Здравствуйте, У нас есть требование для анализа данных, отправленных пользователями по всему миру. Наши системы Linux имеют стандартную локализацию en_US.UTF-8. Тем не менее, мы часто получаем файлы с диакритическими знаками в именах, таких как «special_á_ã_è_characters.doc». Хотя ОС прекрасно справляется с этими файлами, и строка показывает, что ОС передает правильное имя файла программе Java, Java обрабатывает имена и создает исключение «файл не найден», пытаясь открыть их.

Эта простая программа может проиллюстрировать проблему:

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 правильно печатает имя файла (но это неприемлемое решение, поскольку вся система теперь работает на испанском языке.) Явная установка языкового стандарта внутри программы, как показано ниже, не имеет никакого эффекта или. Ниже я изменил программу, чтобы: а) попытаться открыть файл и б) распечатать имя как в 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 для некоторых утилит, таких как bash's echo:

[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 он считывал имя файла таким образом, чтобы его можно было успешно открыть?

Ответы [ 6 ]

8 голосов
/ 18 июня 2010

Во-первых, используемая кодировка не имеет прямого отношения к локали.Таким образом, изменение локали не сильно поможет.

Во-вторых, � типично для символа замены Unicode U + FFFD , печатаемого вместо ISO-8859-1UTF-8.Вот свидетельство:

System.out.println(new String("�".getBytes("UTF-8"), "ISO-8859-1")); // �

Итак, есть две проблемы:

  1. Ваша JVM читает эти специальные символы как .
  2. Ваша консоль используетISO-8859-1 для отображения символов.

Для Sun JVM аргумент VM -Dfile.encoding=UTF-8 должен решить первую проблему.Вторая проблема должна быть исправлена ​​в настройках консоли.Если вы используете, например, Eclipse, вы можете изменить его в Окно> Настройки> Общие> Рабочая область> Кодировка текстового файла .Установите также UTF-8.


Обновление : согласно вашему обновлению:

byte[] textArray = f.getName().getBytes();

Это должно было быть следующим, чтобы исключить влияниекодировка платформы по умолчанию:

byte[] textArray = f.getName().getBytes("UTF-8");

Если это все еще показывает то же самое, то проблема кроется глубже.Какой именно JVM вы используете?Сделайте java -version.Как было сказано ранее, аргумент -Dfile.encoding является специфическим для Sun JVM.Некоторые машины Linux поставляются с GNU JVM или OpenJDK JVM, и тогда этот аргумент может не работать.

3 голосов
/ 16 мая 2011

Это ошибка в JRE / JDK, которая существует годами.

Как исправить java, если отказано открыть файл со специальным символом в имени файла?

File.exists () завершается ошибкой с символами Юникода в имени

Сейчас я повторно отправляю им новый отчет об ошибке, поскольку LC_ALL = en_us исправит некоторые случаи, а некоторые другие не удастся.

2 голосов
/ 24 февраля 2014

Это ошибка в Java-файле old-skool api, может быть, только на Mac?В любом случае, новый java.nio api работает намного лучше.У меня есть несколько файлов, содержащих символы Юникода, которые не удалось загрузить с помощью классов java.io ....После преобразования всего моего кода в java.nio.Path ВСЕ начало работать.И я заменил apache FileUtils (с той же проблемой) на java.nio.Files ...

1 голос
/ 19 ноября 2013

Ну, я был задушен этим вопросом весь день! Мой предыдущий (неправильный) код был таким же, как вы:

for(File f : dir.listFiles()) {
 String filename = f.getName(); // The filename here is wrong !
 FileInputStream fis = new FileInputStream (filename);
}

и это не работает (я использую Java 1.7 Oracle на CentOS 6, LANG и LC_CTYPE = fr_FR.UTF-8 для всех пользователей, кроме zimbra => LANG и LC_CTYPE = C - что, кстати, является причиной этого проблема, но я не могу изменить это без риска, что Zimbra перестанет работать ...)

Поэтому я решил использовать новые классы пакета java.nio.file (Файлы и пути):

DirectoryStream<Path> paths = Files.newDirectoryStream(Paths.get(outputName));
for (Iterator<Path> iterator = paths.iterator(); iterator.hasNext();) {
  Path path = iterator.next();
  String filename = path.getFileName().toString(); // The filename here is correct
  ...
}

Так что, если вы используете Java 1.7, вы должны попробовать новые классы в пакете java.nio.file: это спасло мой день!

Надеюсь, это поможет

1 голос
/ 18 июня 2010

Системное свойство Java file.encoding должно соответствовать кодировке символов консоли.Свойство должно быть установлено при запуске java в командной строке:

java -Dfile.encoding=UTF-8 …

Обычно это происходит автоматически, поскольку консольная кодировка обычно является кодировкой платформы по умолчанию, а Java будет использовать кодировку платформы по умолчанию, еслиВы не указываете это явно.

0 голосов
/ 03 декабря 2013

При использовании DirectoryStream не забудьте закрыть поток (здесь могут помочь try-with-resources)

...