Как создать системные файлы с именами файлов с символами UTF-8 из приложения Tomcat / Java? - PullRequest
2 голосов
/ 09 декабря 2011

У меня есть приложение java-сервера, которое создает имена файловых систем UTF-8.

К сожалению, когда я смотрю на имена файлов, не-ascii символы имен файлов отображаются с '?'.Как заставить систему отображать соответствующие символы UTF-8?

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

Системные характеристики

  • Linux CentOS 6.0 2.6.18.8-xenU # 1 SMP чт 13 мая 11:11:51 PDT 2010 x86_64 x86_64 x86_64 GNU / Linux
  • Tomcat 6
  • Java 1.6

Конфигурации

JAVA_OPTS=-Dsun.jnu.encoding=UTF-8
CATALINA_OPTS=-Dfile.encoding=UTF-8

locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Я даже выполняю следующее при запуске:

System.setProperty("file.encoding", "UTF-8");
System.setProperty("encoding", "UTF-8");
System.setProperty("user.language", "en_US.UTF-8");
System.setProperty("user.country", "en_US.UTF-8");
System.setProperty("sun.jnu.encoding", "UTF8");

И где я создаю файл:

fullPathName = new String(fullPathName.getBytes("UTF-8"));
InputStream is = file.getInputStream();
input = new BufferedInputStream(is, STREAM_BUFFER_SIZE);
output = new BufferedOutputStream(new FileOutputStream(fullPathName),
STREAM_BUFFER_SIZE);

// Read file from memory and write it to disk.
int r;
byte[] buf = new byte[STREAM_BUFFER_SIZE];
while ((r = input.read(buf)) != -1) {
        output.write(buf, 0, r);
}

output.close();
output = null;
input.close();
input = null;

1 Ответ

2 голосов
/ 09 декабря 2011

Мое понимание String в Java состоит в том, что он содержит строку кодовых точек Unicode, внутренне сохраненную как UTF-16.Однако это должно быть деталью реализации для многих методов String.Таким образом, getBytes будет возвращать байтовый массив, содержащий кодировку UTF-8 любых кодовых точек в fullPathName, а затем конструктор String, которому вы передаете эти байты, преобразует его во внутреннюю кодировку String, предполагая, что байты имеют кодировку платформы,Если мы предположим, что вы настроили все так, что Java думает, что UTF-8 является кодировкой платформы, то вы получите строку, содержащую то же содержимое, что и исходная строка.

Так чтовопрос в том, зачем ты это делаешь?Делали ли вы что-то вроде вставки кодовых блоков UTF-8 в строку, а затем ожидаете, что getBytes ("UTF-8") вернет байтовый массив, содержащий именно эти кодовые блоки?

Вы должны проверить, что такое String fullPathNameсодержит, когда вы передаете его в FileOutputStream, потому что наиболее вероятно, что вы делаете что-то, что вызывает передачу неправильной вещи.

Другая возможность состоит в том, что ваша оболочка на самом деле не использует UTF-8Таким образом, когда вы создаете файл с помощью касания, используя греческие символы, вы фактически используете то, что правильно для настройки вашей оболочки.Поэтому, когда Java создает файл с именем, используя кодировку UTF-8, ваша оболочка правильно показывает, что имя файла UTF-8 не соответствует кодировке, для которой настроена оболочка.

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

О, и еще одна вещь.Формат файловой системы оказывает влияние, так что вы можете перечислить это.Хотя я предполагаю, что вы используете какой-то типичный формат файловой системы linux, который не обеспечивает какую-либо кодировку имени файла, некоторые форматы файловой системы, такие как NTFS или HFS +, хранят имена файлов в известной кодировке, и API должны справиться с этим.Например (функция C) при включении может перекодировать из переданного ей байтового массива в UTF-16 с использованием текущей системной кодировки, чтобы выяснить единицы кода UTF-16 для хранения файла в NTFS.Но другие файловые системы не применяют какую-либо кодировку, поэтому fopen просто возьмет предоставленный вами байтовый массив и сохранит его в качестве имени файла.Это приведет к различиям в поведении и может вызвать проблемы с API доступа к файлам в средах, в которых используются строки известных кодировок.Например, если у вас есть функция, которая принимает строку UTF-16 в качестве имени файла и файл, который вы хотите открыть, был назван с использованием строки байтов ISO-8859-1, но системная кодировка UTF-8, то этот файлAPI доступа, вероятно, просто не может открыть этот файл.

Все это просто испорчено.

Я добавляю пример.Следующий файл сохраняется как UTF-8 и называется «HelloWorld.java»

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;

class HelloWorld {
    public static void main(String[] args) {
        String fullPathName = "ΘΙϗϕξ.tmp";
        for(int i=0;i<fullPathName.length();++i) {
            System.out.format("char: %x\n",
                              (int)fullPathName.charAt(i));
        }

        try {
            BufferedOutputStream output =
              new BufferedOutputStream(
                new FileOutputStream(fullPathName));
        } catch(FileNotFoundException e) {
            System.out.println("caught exception");
        }
    }
}

Построен и запущен с использованием javac HelloWorld.java && java HelloWorld, вывод:

char: 152
char: f2
char: 152
char: f4
char: 153
char: f3
char: 153
char: ef
char: 152
char: e6
char: 2e
char: 74
char: 6d
char: 70

Этот вывод указывает, чтонеправильные символы в строке.По-видимому, даже если моя система настроена с использованием языка en_US.UTF-8, в Java не требуется исходный код UTF-8.Построен и запущен с использованием javac -encoding UTF-8 && java HelloWorld Я получаю следующий правильный вывод:

char: 398
char: 399
char: 3d7
char: 3d5
char: 3be
char: 2e
char: 74
char: 6d
char: 70

Теперь строка содержит правильные единицы кода UTF-16 и создает файл "ΘΙϗϕξ.tmp", который отображается в каталоге:

0 [Hydrogen·bames·~/tmp]
⑆ ls
HelloWorld.class
HelloWorld.java
ΘΙϗϕξ.tmp
0 [Hydrogen·bames·~/tmp]
⑆ ls *.tmp | hexdump -C
00000000  ce 98 ce 99 cf 97 cf 95  ce be 2e 74 6d 70 0a     |...........tmp.|
0000000f

Как видите, FileOutputStream правильно преобразован в кодировку локали для создания файла, поскольку ce 98 - это правильная кодировка UTF-8 U+0398 или 'Θ'.

Не ясно, является ли имя файла, правильно отображаемое в ваших файлах журнала, достаточным, чтобы сказать, что содержимое строки действительно в порядке.Также было бы полезно узнать имя файла, которое вы получаете больше, чем просто то, что некоторые символы выглядят как «?».Какие фактические значения хранятся?Вы можете использовать hexdump, чтобы узнать.

...