File.listFiles () изменяет имена Unicode с помощью JDK 6 (проблемы с нормализацией Unicode)

Я борюсь со странным именем файла проблема кодирования при перечислении содержимого каталога в Java 6 как в OS X, так и в Linux: файл . listFiles () и связанные с ним методы, похоже, возвращают имена файлов в кодировке, отличной от кодировки остальной системы.

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

Вот программа для демонстрации. Он создает файл с именем Unicode, затем распечатывает URL-кодированные версии имен файлов, полученных из непосредственно созданного файла, и тот же файл, если он указан в родительском каталоге (вы должны запустить этот код в пустой каталог). Результаты показывают различную кодировку, возвращаемую методом File.listFiles () .

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"));
}

Здесь ' что я получаю, когда запускаю этот тестовый код в своих системах. Обратите внимание на представления символов % CC и % C3 .

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 (работает на виртуальной машине в той же системе 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)

Я пробовал различные приемы для согласования строк, включая установку системного свойства file.encoding и различных переменных среды LC_CTYPE и LANG . Ничего не помогает, и я не хочу прибегать к таким хитростям.

В отличие от этого (отчасти связанного?) Вопроса , я могу читать данные из перечисленных файлов, несмотря на странные имена

36
задан Community 23 May 2017 в 12:16
поделиться

2 ответа

В файловой системе Unix имя файла на самом деле представляет собой байт с завершающим нулем[]. Таким образом, среда выполнения java должна выполнять преобразование из java.lang.String в byte[] во время операции createNewFile(). Преобразование символов в байты регулируется локалью. Я тестировал настройку LC_ALL на en_US.UTF-8 и en_US.ISO-8859-1 и получил согласованные результаты. Это с Sun (... Oracle) java 1.6.0_20. Однако для LC_ALL=en_US.POSIX результат будет следующим:

File name:   Tr%C3%AEcky+N%C3%A5me
Listed name: Tr%3Fcky+N%3Fme

3F — знак вопроса. Он говорит мне, что преобразование не было успешным для символа, отличного от ASCII. Потом опять все ожидаемо.

Но причина, по которой ваши две строки различаются, заключается в эквивалентности между символом \u00EE (или C3 AE в UTF-8) и последовательностью i+\u0302 (69 CC 82 в UTF-8). \u0302 — это комбинированный диакритический знак (сочетающий циркумфлексный ударение). Некоторая нормализация произошла во время создания файла. Я не уверен, делается ли это во время выполнения Java или в ОС.

ПРИМЕЧАНИЕ. Мне потребовалось некоторое время, чтобы понять это, поскольку фрагмент кода, который вы опубликовали, не имеет комбинированного диакритического знака, а эквивалентен символу î (например, >ee). Вы должны были внедрить escape-последовательность Unicode в строковый литерал (но это легко сказать потом...).

0
ответ дан 27 November 2019 в 06:18
поделиться

Я подозреваю, что вам просто нужно указать javac, какую кодировку использовать для компиляции файла .java, содержащего специальные символы, поскольку вы жестко закодировал его в исходном файле. В противном случае будет использоваться кодировка платформы по умолчанию, которая может вообще не быть UTF-8.

Для этого можно использовать аргумент VM -encoding.

javac -encoding UTF-8 com/example/Foo.java

Таким образом, результирующий файл .class будет содержать правильные символы, и вы также сможете создать и указать правильное имя файла.

0
ответ дан 27 November 2019 в 06:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: