Java не может открыть файл с суррогатными значениями Unicode в имени файла?
Я имею дело с кодом, который выполняет различные операции ввода-вывода с файлами, и я хочу, чтобы он мог обрабатывать международные имена файлов. Я работаю над Mac с Java 1.5, и если имя файла содержит символы Unicode, которые требуют суррогатов, JVM не может найти файл. Например, мой тестовый файл:
"草鷗外.gif"
, который разбивается на символы Java \u8349\uD85B\uDFF6\u9DD7\u5916.gif
Если я создаю файл из этого имени файла, я не могу его открыть, потому что получаю исключение FileNotFound. Даже использование этого в папке, содержащей файл, не будет выполнено:
File[] files = folder.listFiles();
for (File file : files) {
if (!file.exists()) {
System.out.println("Failed to find File"); //Fails on the surrogate filename
}
}
Большая часть кода, на котором я нахожусь, имеет форму:
FileInputStream instream = new FileInputStream(new File("草鷗外.gif"));
// operations follow
Можно ли каким-то образом решить эту проблему, либо скрыть имена файлов, либо открыть файлы по-разному?
Ответы
Ответ 1
Я подозреваю, что один из Java или Mac использует CESU-8 вместо правильного UTF-8. Java использует "измененный UTF-8" (который является небольшим вариантом CESU-8) для различных внутренних целей, но я не знал, что он может использовать его как файловую систему /defaultCharset. К сожалению, у меня нет ни Mac, ни Java для тестирования.
"Modified" is a modified way of saying "badly bugged". Instead of outputting a four-byte UTF-8 sequence for supplementary (non-BMP) characters like 𦿶:
\xF0\xA6\xBF\xB6
он выводит последовательность с кодировкой UTF-8 для каждого из суррогатов:
\xED\xA1\x9B\xED\xBF\xB6
Это не действительная последовательность UTF-8, но многие декодеры позволят это в любом случае. Проблема в том, что если вы совершите кругосветное путешествие через настоящий кодировщик UTF-8, у вас есть другая строка, четырехбайтная одна выше. Попробуйте получить доступ к файлу с таким именем и бумом! потерпеть неудачу.
Итак, сначала давайте просто проверить, как имена файлов фактически хранятся в вашей текущей файловой системе, используя платформу, которая использует байты для имен файлов, таких как Python 2.x:
$ python
Python 2.x.something (blah blah)
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.listdir('.')
On my filesystem (Linux, ext4, UTF-8), the filename "草𦿶鷗外.gif" comes out as:
['\xe8\x8d\x89\xf0\xa6\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif']
что вы хотите. Если это то, что вы получаете, вероятно, это делает Java неправильно. Если вы получите более длинную версию с шестью байтами:
['\xe8\x8d\x89\xed\xa1\x9b\xed\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif']
возможно, OS X делает это неправильно... всегда ли он хранит имена файлов? (Или файлы были откуда-то изначально изначально?) Что делать, если вы переименуете файл в "правильную версию?":
os.rename('\xe8\x8d\x89\xed\xa1\x9b\xed\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif', '\xe8\x8d\x89\xf0\xa6\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif')
Ответ 2
Если ваша локальная среда по умолчанию не включает эти символы, вы не можете открыть файл.
Смотрите: Ошибка файла .exists() с символами unicode в имени
Edit:
Хорошо. Вам нужно изменить системный язык. Какую бы ОС вы ни использовали.
Изменить:
Смотрите: Как открыть файлы с акцентами в Java?
Смотрите: JFileChooser на Mac не может видеть файлы, названные китайскими символами?
Ответ 3
Это оказалось проблемой с Mac JVM (проверено на 1.5 и 1.6). Имена файлов, содержащие дополнительные символы/суррогатные пары, не могут быть доступны с помощью класса Java File. Я закончил писать библиотеку JNI с вызовами Carbon для версии Mac проекта (ick). Я подозреваю, что проблема CESU-8, упомянутая выше, поскольку вызов JNI для получения символов UTF-8 возвращает строку CESU-8. Не похоже на то, что вы действительно можете обойти.
Ответ 4
Это ошибка в java файле старого java файла api, возможно, только на Mac? Во всяком случае, новый java.nio api работает намного лучше. У меня есть несколько файлов, содержащих символы и содержимое Unicode, которые не загружаются с использованием java.io.File и связанных с ним классов. После преобразования всего моего кода для использования java.nio.Path ВСЕ начало работать. И я заменил org.apache.commons.io.FileUtils(который имеет ту же проблему) с java.nio.Files...
... и обязательно прочитайте и напишите содержимое файла с помощью соответствующей кодировки, например: Files.readAllLines(myPath, StandardCharsets.UTF_8)