Понимание кодировки символов в типичном веб-приложении Java - PullRequest
5 голосов
/ 29 марта 2010

Какой-то псевдокод:

String a = "A bunch of text"; //UTF-16
saveTextInDb(a); //Write to Oracle VARCHAR(15) column
String b = readTextFromDb(); //UTF-16
out.write(b); //Write to http response

Когда вы сохраняете Java String (UTF-16) в Oracle VARCHAR (15), Oracle также сохраняет это как UTF-16? Относится ли длина Oracle VARCHAR к числу символов Unicode (а не к числу байтов)?

Когда мы пишем b в ServletResponse, это записывается как UTF-16 или мы по умолчанию конвертируем в другую кодировку, такую ​​как UTF-8?

Ответы [ 3 ]

4 голосов
/ 29 марта 2010

Способность Oracle сохранять (и позднее извлекать) текст Unicode из базы данных зависит только от набора символов базы данных (обычно указывается при создании базы данных). Выбор AL32UTF8 в качестве набора символов рекомендуется для хранения текста Unicode в типах данных CHAR (включая VARCHAR / VARCHAR2), поскольку он позволит вам получить доступ ко всем кодовым точкам Unicode, не занимая при этом много места для хранения по сравнению с другими кодировками, такими как AL16UTF16 / AL32UTF32.

Предполагая, что это сделано, драйвер Oracle JDBC отвечает за преобразование данных в кодировке UTF-16 в AL32UTF8. Это «автоматическое» преобразование между кодировками также происходит, когда данные считываются из базы данных. Чтобы ответить на запрос о длине байта VARCHAR, определение столбца VARCHAR2 в Oracle включает семантику байтов - VARCHAR2 (n) используется для определения столбца, который может хранить n байтов (это поведение по умолчанию, как указано параметром NLS_LENGTH_SEMANTICS). базы данных); если вам нужно определить размер, основанный на символах, следует использовать VARCHAR2 (n CHAR).

Кодировка данных, записанных в объект ServletResponse, зависит от кодировки символов по умолчанию, если это не указано с помощью ServletResponse.setCharacterEncoding () или ServletResponse.setContentType () Вызовы API. В общем, для полного решения Unicode, включающего базу данных Oracle, необходимо знать

  1. Кодировка входящих данных (то есть кодировка данных, считываемых через объект ServletRequest). Это можно сделать, указав принятую кодировку в формах HTML через атрибут accept-charset . Если кодировка неизвестна, приложение может попытаться установить для нее известное значение с помощью метода ServletRequest.setCharacterEncoding () . Этот метод не меняет существующую кодировку символов в потоке. Если входной поток имеет формат ISO-Latin1, указание другой кодировки, скорее всего, приведет к возникновению исключения. Знание кодировки важно, так как библиотеки времени выполнения Java потребуют знания исходной кодировки потока, если содержимое потока нужно обрабатывать как символьные примитивы или строки. По-видимому, это требуется, когда вы вызываете ServletRequest.getParameter или аналогичные методы, которые будут обрабатывать поток и возвращать объекты String. Процесс декодирования приведет к созданию символов в кодировке платформы (это UTF-16).
  2. Кодирование данных, считываемых из потоков, в отличие от данных, созданных в JVM. Это очень важно, поскольку кодирование данных, считываемых из потоков, не может быть изменено. Однако существует процесс декодирования, который преобразует символы в поддерживаемых кодировках в символы UTF-16 всякий раз, когда к таким данным обращаются как примитив символа или как строка. Новые объекты String, с другой стороны, могут быть созданы с определенной кодировкой. Это имеет значение, когда вы записываете содержимое потока в другой поток (например, выходной поток объекта HttpServletResponse). Если содержимое входного потока обрабатывается как последовательность байтов, а не как символы или строки, то JVM не будет предпринимать никаких операций декодирования. Это будет означать, что байты, записанные в выходной поток, не должны изменяться, если промежуточный символ или объекты String не созданы. В противном случае вполне возможно, что содержимое выходного потока будет искажено и неправильно проанализировано соответствующим декодером. Проще говоря,

    • если кто-то записывает объекты String или символы в выходной поток сервлета, то необходимо указать кодировку, которую браузер должен использовать для декодирования ответа. Соответствующие кодеры могут использоваться для кодирования последовательности символов, указанной в желаемом ответе.
    • если кто-то записывает последовательность байтов, которые будут интерпретироваться как символы, то код, который должен быть указан в заголовке HTTP, должен быть известен заранее
    • если кто-то записывает последовательность байтов, которые будут анализироваться как последовательность байтов (для изображений и других двоичных данных), то концепция кодирования не имеет значения.
  3. Набор символов базы данных экземпляра Oracle. Как указывалось ранее, данные будут храниться в базе данных Oracle в определенном наборе символов (для типов данных CHAR). Драйвер Oracle JDBC обеспечивает преобразование данных между UTF-16 и AL32UTF8 (набор символов базы данных в данном случае) для типов данных CHAR и NCHAR. Когда вы вызываете resultSet.getString(), драйвер JDBC возвращает строку с символами UTF-16. Обратное верно, когда вы отправляете данные в базу данных тоже. Если используется другой набор символов базы данных, дополнительный драйвер преобразования (от UTF-16 до UTF-8 в набор символов базы данных) выполняется драйвером JDBC прозрачно.
4 голосов
/ 29 марта 2010

Вместо UTF-16 подумайте о «внутреннем представлении» вашей строки. Строка в Java - это какие-то символы, вам все равно, какая кодировка используется внутри. Кодирование становится актуальным, если вы взаимодействуете с внешним миром программы. В вашем примере saveTextInDb, readTextFromDb и write делают это. Каждый раз, когда вы обмениваетесь строками с внешней стороной, используется кодировка для преобразования. saveTextInDb (и read) выглядят как самодельные методы, по крайней мере, я их не знаю. Поэтому вы должны посмотреть, какая кодировка используется для этих методов. Метод write of Writer всегда создает байты, которые представляют кодировку, связанную с Writer. Если вы получаете ваш Writer от HttpServletResponse, ассоциированная кодировка используется для вывода ответа (который будет отправлен в заголовках).

response.setEncoding("UTF-8");
Writer out = response.getWriter();

Этот код возвращается без Writer, который переводит строки в кодировку UTF-8. Похоже если пишешь в файл:

Writer fileout = new OutputStreamWriter(new FileOutputStream(myfile), "ISO8859-1");

Если вы обращаетесь к БД, используемая вами структура должна обеспечивать согласованный обмен строками с базой данных.

3 голосов
/ 29 марта 2010

ServletResponse будет использовать ISO 8859-1 (латиница 1) по умолчанию. UTF-8 - наиболее распространенная кодировка, используемая для ответов HTTP, для которых требуется Unicode, но вы должны установить эту кодировку специально.

Согласно этому документу Oracle может поддерживать в базе данных либо UTF-8, либо UTF-16. Ваши методы для чтения / записи Oracle должны будут использовать соответствующую кодировку, которая соответствует настройке базы данных, и преобразовать ее в / из внутреннего представления Java.

...