Аннотация
Как известно, Java использует представление строк в формате Unicode. В
этой статье речь пойдет о способах хранения и представления данных,
записанных в разных кодировках.
Java по-русски
Написать java-апплет, выводящий приветствие на русском языке очень
просто. Можно вставить строку с русским языком прямо внутрь текста
класса:
String labelName = "Привет!";
JLabel myLabel = new JLabel(labelName);
Однако не факт, что это будет работать. Ведь тот русский текст,
который был набран в редакторе, мог существовать практически в любой
кодировке (DOS/Win/UNIX). Исходный текст программы обычно представлен
в 8-битном формате, а один символ (char) в java занимает 2 байта (это
связано с тем, что язык java ориентирован на поддержку и на работу с
Unicode). Таким образом при компиляции программы происходит
преобразование символов из одной кодировки (кодировки редактора) в
другую (Unicode).
Это преобразование (перекодировка) основывается на том, что исходный
текст программы написан в той же кодировке, что и базовая кодировка
Java Virtual Machine (JVM), которая, как правило, совпадает с
кодировкой самой операционной системы (для русских Windows это будет
Cp1251). Таким образом, если кодировка кириллицы в редакторе совпала
с кодировкой кириллицы в системе, то сообщение ``Привет!'' появится в
нормальном виде.
Чтобы избежать путаницы с кодировками русского языка в тексте
программы, у компилятора javac есть специальный
параметр encoding, который определяет кодировку
исходного текста программы.
Есть и другой способ. Русские буквы в тексте программы можно указывать
в формате `\uXXXX' (где XXXX -- шестнадцатиричный код символа в
стандарте Unicode). В комплект JDK входит утилита
native2ascii, которая позволяет перевести текст из
любой поддерживаемых JDK кодировок в ASCII-вид, а символы, не имеющие
отображения в ASCII, представить в `\uXXXX' виде. Узнать
код того или иного символа кириллицы в Unicode можно, запустив
программу charmap в WindowsNT.
Русский текст из внешнего источника
Как показать русский текст из внешнего источника, например файла?
Текст в файле хранится, как правило, в 8-битном формате. Для
чтения/записи и преобразования набора байт в строку и обратно
используются специальные классы из пакета java.io:
InputStreamReader и OutputStreamReader.
Эти два класса специально предназначены для преобразования потока байт
в указанной кодировке (чем по умолчанию является кодировка
системы) в Unicode строку java.
Вот типичный пример создания ``читателя'' из файла:
FileInputStream fis =
new FileInputStream("priwet.txt");
InputStreamReader isr =
new InputStreamReader(fis, "Cp1251");
Reader in = new BufferedReader(isr);
Unicode и Java
Предположим, однако, что необходимо работать с внешним текстом (или
данными) не только в русской, а в любой кодировке. Эту проблему можно
решить двумя способами: храня внешние данные в формате Unicode
или же в формате MBCS (MultiByte Character Set).
MBCS
Несмотря на то, что Unicode был специально разработан для поддержки
многих языков, использовать его для хранения и передачи строк в
Америке и большей части Европы неэффективно, потому что для этих
регионов достаточно 256, а иногда всего 128 символов. Ведь первые 128
символов Unicode (\u0000 -- \u007F) совпадают с ISO-646, а первые 256
символов (\u0000 -- \u007F) совпадают с ISO-8859-1. Тем самым,
используется только маленький кусочек всего спектра Unicode, ведь
старший байт почти всегда равен нулю.
Возникает желание по-прежнему использовать Unicode внутри
программы, но хранить и передавать данные в 8-битной кодировке,
преобразуя данные непосредственно перед их получением или записью. В
качестве записи в 8-битной кодировке можно использовать технологию
MBCS, где каждый символ может занимать несколько байт.
Вопрос заключается в том, какой набор символов (кодировку)
использовать для такого преобразования. Существующие кодировки не
подходили для этой цели, поэтому Unicode Consortium выработал
специальные кодировки для преобразования Unicode-строк в
MBCS-строки. Эти кодировки носят название Unicode Transformation
Format (UTF), и существуют в двух вариантах: UTF-7 и UTF-8. Они
различаются количеством бит (7 или 8), используемых для кодирования.
Кодировка UTF-7 характерна тем, что часто требует больше байтов для
представления данных, чем сам Unicode. Но эта кодировка
необходима, так как многие старые системы (и не только, например,
MIME) используют 7-битную кодировку символов.
Кодировка UTF-8, в свою очередь, очень хорошо подходит для хранения
текста, который используют ASCII символы, и символы, чей Unicode код
меньше \u0800. В этом спектре лежит большинство символов,
используемых в европейских (соответственно, и американских) и
ближневосточных странах. Для символов, чей код больше \u07FF, UTF-8,
наоборот, мало подходит, потому что эти символы расплываются до 3
байт.
Алгоритмы конвертации между Unicode и UTF-7 или UTF-8 описаны в
стандарте Unicode (The Unicode Standart).
Unicode
Чтобы узнать, как будет выглядеть текстовый файл в формате Unicode,
можно запустить программу notepad в WindowsNT и
при сохранении текстового файла поставить галочку ``Сохранить в
формате Unicode''.
Так как каждый символ занимает 2 байта, то возникает проблема, в каком
порядке они должны быть записаны. Рассмотрим строку ``Привет!''.
Возможны два варианта:
- младший байт впереди (little endian)
04 40 04 38 04 32 04 35 04 42 04 21 00
- старший байт впереди (big endian)
40 04 38 04 32 04 35 04 42 04 21 04 00
Какой из вариантов является правильным? В стандарте Unicode
написано, что порядок байт по умолчанию является либо big endian, либо
little endian :-)
Действительно, оба порядка являются правильным, и разработчики систем
сами выбирают себе один из них. Так что нечего беспокоиться, если ваша
Windows NT Workstation обменивается данными с Windows NT Server -- они
обе используют little endian.
Однако, если ваша Windows NT Workstation обменивается данными с
UNIX-сервером, который использует big endian, одна из систем должна
осуществлять перекодировку. В этом случае стандарт Unicode
гласит, что можно выбрать любой из следующих способов решения
проблемы:
-
Когда две системы, использующие различный порядок представления
байт в Unicode, обмениваются данными (не используя каких-то
специальных протоколов), то порядок байт должен быть big endian. В
стандарте это называется каноническим порядком байт.
Однако второй способ является более универсальным и
предпочтительным:
- Каждая строка Unicode должна начинаться с кода
\uFEFF, который в стандарте называется ``знаком порядка''
(byte order mark) или ``zero-width nonbreaking space''. Код
\uFFFE, который является ``перевертыванием'' знака
порядка, не обозначает ничего (так он зарезервирован в стандарте
Unicode). Поэтому если получатель видит в качестве первого
символа код \uFEFF, то это значит, что байты находятся в
перевернутом (little endian) порядке.
Вот как выглядит файл со строкой ``Привет!'', сохраненный в формате Unicode:
 |
Для чтения данных, записанных как в формате MBCS (используя
кодировку UTF-8), так и в формате Unicode, можно использовать все тот
же класс InputStreamReader из пакета java.io, подставляя в его
конструктор различные кодировки. В описании пакета
java.lang
написано, что каждаяН реализация JVM должна поддерживать
следующие кодировки:
| ННН |
US-ASCII |
-- семибитная ASCII, она же ISO646-US, она же основная латинская часть
Unicode; |
| ISO-8859-1 |
-- то же самое, что ISO-LATIN-1;
|
| UTF-8 |
-- 8-битный Unicode Transformation Format;
|
| UTF-16BE |
-- 16-битный Unicode Transformation Format, порядок байт big-endian;
|
| UTF-16LE |
-- 16-битный Unicode Transformation Format, порядок байт little-endian;
|
| UTF-16 |
-- 16-битный Unicode Transformation Format, порядок байт определяется начальными значениями (допускается любой), на выходе порядок байт big-endian.
|
Для чтения файла в формате Unicode надо использовать значение
"UTF-16", а для чтения файла в формате UTF-8 - использовать "UTF-8":
FileInputStream fis =
new FileInputStream("priwet_u.txt");
InputStreamReader isr =
new InputStreamReader(fis, "UTF-16");
Преобразование между строками и потоком байт
В java для преобразования потока байт (byte[]) в строку (String) и
обратно, в классе String есть следующие возможности:
- Конструктор String(byte[] bytes, String enc) получает
на вход поток байт с указанием их кодировки; если кодировку опустить
то она будет принята по умолчанию
- Метод getBytes(String enc) возвращает поток байт,
записанных в указанной кодировке; кодировку также можно опустить.
try {
String priwet = new String(
"\u041F"+"\u0440"+"\u0438"+
"\u0432"+"\u0435"+"\u0442"+"!");
byte[] utf8Bytes = priwet.getBytes("UTF8");
String priwet2 = new String(utf8Bytes,"UTF8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Дата последнего обновления: Август 2000.
Copyright № 1999 - 2001, Javable.com. |