Таблица кодировки русскоязычных символов. Кодировка кириллицы - русского языка

Таблица кодировки русскоязычных символов. Кодировка кириллицы - русского языка

Кодировка представляет собой таблицу символов, где каждой букве алфавита (а также цифрам и специальным знакам) присвоен свой уникальный номер - код символа.

Стандартизирована только половина таблицы, т.н. ASCII-код - первые 128 символов, которые включают в себя буквы латинского алфавита. И с ними никогда не бывает проблем. Вторая же половина таблицы (а всего в ней 256 символов - по количеству состояний, который может принять один байт) отдана под национальные символы, и в каждой стране эта часть различна. Но только в России умудрились придумать целых 5 различных кодировок. Термин "различные" обозначает то, что одному и тому же символу соответствует разный цифровой код. Т.е. если мы неправильно определим кодировку текста, то нашему вниманию предстанет абсолютно нечитаемый текст.

Кодировки появились исторически. Первая широко используемая российская кодировка называлась KOI-8 . Ее придумали, когда адаптировали к русскому языку систему UNIX. Это было еще в семидесятых - до появления персоналок. И до сих пор в UNIX это считается основной кодировкой.

Потом появились первые персональные компьютеры, и началось победное шествие DOS. Вместо того чтобы воспользоваться уже придуманной кодировкой, Microsoft решила сделать свою, ни с чем не совместимую. Так появилась DOS-кодировка (или 866 кодовая страница). В ней, кстати, были введены спецсимволы для рисования рамок, что широко использовалось в программах написанных под DOS. Например, в том же Norton Commander-е.

Параллельно с IBM-совместимыми развивались и Macintosh-компьютеры. Несмотря на то, что их доля в России очень мала, тем не менее, потребность в русификации существовала и, разумеется, была придумана еще одна кодировка - MAC .

Время шло, и 1990 году Microsoft явила на свет первую успешную версию Windows 3.0-3.11. А вместе с ней и поддержку национальных языков. И снова был проделан такой же фокус, как и с DOS. По непонятным причинам они не поддержали ни одну, из уже существовавших ранее (как это сделала OS/2, принявшая за стандарт DOS-кодировку), а предложили новую Win-кодировку (или кодовая страница 1251 ). Де-факто, она стала самой распространенной в России.

И, наконец, пятый вариант кодировки связан уже не с конкретной фирмой, а с попытками стандартизации кодировок на уровне всей планеты. Занималась этим ISO - международная организация по стандартам. И, догадайтесь, что они сделали с русским языком? Вместо того, чтобы принять за "стандартную русскую" какую-нибудь из вышеописанных, они придумали еще одну (!) и назвали ее длинным неудобоваримым сочетанием ISO-8859-5 . Разумеется, она тоже оказалась ни с чем не совместимой. И в настоящий момент эта кодировка практически нигде не применяется. Кажется, ее используют только в базе данных Oracle. По крайней мере, я ни разу не видел текст в этой кодировке. Тем не менее, ее поддержка присутствует во всех броузерах.

Сейчас идет работа над созданием новой универсальной кодировки (UNICODE ), в которой предполагается в одну кодовую таблицу запихнуть все языки мира. Тогда точно проблем не будет. Для этого на каждый символ отвели 2 байта. Таким образом, максимальное количество знаков в таблице расширилось до 65535. Но до момента, когда все перейдут на UNICODE, остается еще слишком много времени.

Здесь немного отвлекемся и расмотрим для целостного восприятия мета тег - Content-Type.

Мета теги используются для описания свойств HTML документа и должны находится в рамках тега HEAD. Мета теги типа NAME содержат текстовую информацию о документе, его авторе и некоторые рекомендации для поисковых машин. Например: Robots, Description, Keywords, Author, Copyright.

Мета теги типа HTTP-EQUIV влияют на формирование заголовка документа и определяют режим его обработки.

Мета тег Content-Type - Отвечает за указание типа документа и кодировки символов.

Использовать мета тег Content-Type надо только с учетом некоторых нюансов:

Во - первых, кодировка символов текста должна соответствовать кодировке, указанной в теге.

Во - вторых, сервер не должен менять кодировку текста при обработке запроса броузера.

В - третьих, если сервер меняет кодировку текста, он должен скорректировать или удалить мета тег Content-Type.

Несоблюдение этих требований может привести к следующему: web-сервер автоматически определит кодировку запроса клиента и отдаст страничку web-броузеру перекодированной. Броузер, в свою очередь, будет читать документ в соответствии с мета тегом Content-Type. И если кодировки не совпадут, то прочитать документ можно будет только после ряда замысловатых манипуляций. Особенно это характерно для старых броузеров.

Внимание! Мета тег Content-Type очень часто вставляется генераторами HTML кода.

Наиболее часто встречаются типы кодировки:

Windows-1251 - Кириллица (Windows).

KOI8-r - Кириллица (КОИ8-Р)

cp866 - Кириллица (DOS).

Windows-1252 - Западная Европа (Windows).

Windows-1250 - Центральная Европа (Windows).

Наверняка всем известен мета тег -

В данном материале использовались выдержки из статьи с сайта http://cherry-design.ru/

Недавно освободившиеся домены с PR и ТИЦ:

Сервис http://reg.ru - крупнейшего хостинга и регистратора доменов позволяет подать заявку на регистрацию доменного имени, которое недавно было освобождено прежним Администратором. Освобожденные домены часто имеют высокие показатили ТИЦ и PR и могут быть интересны к приобретению.

Освобожденные домены.RU c ТИЦ: Свободные премиум-домены: Объем информации: 7659 bytes

Кодировки

Когда я только начинал программировать на языке C, первой моей программой (не считая HelloWorld) была программа перекодировки текстовых файлов из основной кодировки ГОСТ-а (помните такую? :-) в альтернативную. Было это в далёком 1991-ом году. С тех пор многое изменилось, но за прошедшие 10 лет подобные программки свою актуальность, к сожалению, не потеряли. Слишком много уже накоплено данных в разнообразных кодировках и слишком много используется программ, которые умеют работать только с одной. Для русского языка существует не менее десятка различных кодировок, что делает проблему ещё более запутанной.

Откуда же взялись все эти кодировки и для чего они нужны? Компьютеры по своей природе могут работать только с числами. Для того чтобы хранить буквы в памяти компьютера надо поставить в соответствие каждой букве некое число (примерно такой же принцип использовался и до появления компьютеров - вспомните про ту же азбуку Морзе). Причём число желательно поменьше - чем меньше двоичных разрядов будет задействовано, тем эффективнее можно будет использовать память. Вот это соответствие набора символов и чисел собственно и есть кодировка. Желание любой ценой сэкономить память, а так же разобщённость разных групп компьютерщиков и привела к нынешнему положению дел. Самым распространённым способом кодирования сейчас является использование для одного символа одного байта (8 бит), что определяет общее кол-во символов в 256. Набор первых 128 символов стандартизован (набор ASCII) и является одинаковыми во всех распространённых кодировках (те кодировки, где это не так уже практически вышли из употребления). Англицкие буковки и символы пунктуации находятся в этом диапазоне, что и определяет их поразительную живучесть в компьютерных системах:-). Другие языки находятся не в столь счастливом положении - им всем приходится ютиться в оставшихся 128 числах.

Unicode

В конце 80-х многие осознали необходимость создания единого стандарта на кодирование символов, что и привело к появлению Unicode. Unicode - это попытка раз и навсегда зафиксировать конкретное число за конкретным символом. Понятно, что в 256 символов тут не уложишься при всём желании. Довольно долгое время казалось, что уж 2-х то байт (65536 символов) должно хватить. Ан нет - последняя версия стандарта Unicode (3.1) определяет уже 94140 символов. Для такого кол-ва символов, наверное, уже придётся использовать 4 байта (4294967296 символов). Может быть и хватит на некоторое время. :-)

В набор символов Unicode входят всевозможные буквы со всякими чёрточками и припендюльками, греческие, математические, иероглифы, символы псевдографики и пр. и пр. В том числе и так любимые нами символы кириллицы (диапазон значений 0x0400-0x04ff). Так что с этой стороны никакой дискриминации нет.

Если Вам интересны конкретные кода символов, для их просмотра удобно использовать программу "Таблица символов" из WinNT. Вот, например, диапазон кириллицы:

Если у Вас другая OS или Вас интересует официальное толкование, то полную раскладку символов (charts) можно найти на официальном сайте Unicode (http://www.unicode.org/charts/web.html).

Типы char и byte

В Java для символов выделен отдельный тип данных char размером в 2 байта. Это часто порождает путаницу в умах начинающих (особенно если они раньше программировали на других языках, например на C/C++). Дело в том, что в большинстве других языков для обработки символов используются типы данных размером в 1 байт. Например, в C/C++ тип char в большинстве случаев используется как для обработки символов, так и для обработки байтов - там нет разделения. В Java для байтов имеется свой тип - тип byte . Таким образом C-ишному char соответствует Java-вский byte , а Java-вскому char из мира C ближе всего тип wchar_t . Надо чётко разделять понятия символов и байтов - иначе непонимание и проблемы гарантированны.

Java практически с самого своего рождения использует для кодирования символов стандарт Unicode. Библиотечные функции Java ожидают увидеть в переменных типа char символы, представленные кодами Unicode. В принципе, Вы, конечно, можете запихнуть туда что угодно - цифры есть цифры, процессор всё стерпит, но при любой обработке библиотечные функции будут действовать исходя из предположения что им передали кодировку Unicode. Так что можно спокойно полагать, что у типа char кодировка зафиксирована. Но это внутри JVM. Когда данные читаются извне или передаются наружу, то они могут быть представлены только одним типом - типом byte . Все прочие типы конструируются из байтов в зависимости от используемого формата данных. Вот тут то на сцену и выходят кодировки - в Java это просто формат данных для передачи символов, который используется для формирования данных типа char . Для каждой кодовой страницы в библиотеке имеется по 2 класса перекодировки (ByteToChar и CharToByte). Классы эти лежат в пакете sun.io . Если, при перекодировке из char в byte не было найдено соответствующего символа, он заменяется на символ?.

Кстати, эти файлы кодовых страниц в некоторых ранних версиях JDK 1.1 содержат ошибки, вызывающие ошибки перекодировок, а то и вообще исключения при выполнении. Например, это касается кодировки KOI8_R. Лучшее, что можно при этом сделать - сменить версию на более позднюю. Судя по Sun-овскому описанию, большинство этих проблем было решено в версии JDK 1.1.6.

До появления версии JDK 1.4 набор доступных кодировок определялся только производителем JDK. Начиная с 1.4 появилось новое API (пакет java.nio.charset), при помощи которого Вы уже можете создать свою собственную кодировку (например поддержать редко используемую, но жутко необходимую именно Вам).

Класс String

В большинстве случаев для представления строк в Java используется объект типа java.lang.String . Это обычный класс, который внутри себя хранит массив символов (char), и который содержит много полезных методов для манипуляции символами. Самые интересные - это конструкторы, имеющие первым параметром массив байтов (byte) и методы getBytes() . При помощи этих методов Вы можете выполнять преобразования из массива байтов в строки и обратно. Для того, чтобы указать какую кодировку при этом использовать у этих методов есть строковый параметр, который задаёт её имя. Вот, например, как можно выполнить перекодировку байтов из КОИ-8 в Windows-1251:

Список 8-ми битовых кодировок, доступных в современных JDK и поддерживающих русские буквы Вы можете найти ниже, в разделе .

Так как кодировка - это формат данных для символов, кроме знакомых 8-ми битовых кодировок в Java также на равных присутствуют и многобайтовые кодировки. К таким относятся UTF-8, UTF-16, Unicode и пр. Например вот так можно получить байты в формате UnicodeLittleUnmarked (16-ти битовое кодирование Unicode, младший байт первый, без признака порядка байтов):

При подобных преобразованиях легко ошибиться - если кодировка байтовых данных не соотвествуют указанному параметру при преобразовании из byte в char , то перекодирование будет выполнено неправильно. Иногда после этого можно вытащить правильные символы, но чаще всего часть данных будет безвозвратно потеряна.

В реальной программе явно указывать кодовую страницу не всегда удобно (хотя более надёжно). Для этого была введена кодировка по умолчанию. По умолчанию она зависит от системы и её настроек (для русских виндов принята кодировка Cp1251), и в старых JDK её можно изменить установкой системного свойства file.encoding . В JDK 1.3 изменение этой настройки иногда срабатывает, иногда - нет. Вызвано это следующим: первоначально file.encoding ставится по региональным настройкам компьютера. Ссылка на кодировку по умолчанию запоминается в нутрях при первом преобразовании. При этом используется file.encoding, но это преобразование происходит ещё до использования аргументов запуска JVM (собсно, при их разборе). Вообще-то, как утверждают в Sun, это свойство отражает системную кодировку, и она не должна изменяться в командной строке (см., например, комментарии к BugID ) Тем не менее в JDK 1.4 Beta 2 смена этой настройки опять начала оказывать эффект. Что это, сознательное изменение или побочный эффект, который может опять исчезнуть - Sun-овцы ясного ответа пока не дали.

Эта кодировка используется тогда, когда явно не указанно название страницы. Об этом надо всегда помнить - Java не будет пытаться предсказать кодировку байтов, которые Вы передаёте для создания строки String (так же она не сможет прочитать Ваши мысли по этому поводу:-). Она просто использует текущую кодировку по умолчанию. Т.к. эта настройка одна на все преобразования, иногда можно наткнуться на неприятности.

Для преобразования из байтов в символы и обратно следует пользовать только этими методами. Простое приведение типа использовать в большинстве случаев нельзя - кодировка символов при этом не будет учитываться. Например, одной из самых распространённых ошибок является чтение данных побайтно при помощи метода read() из InputStream, а затем приведение полученного значения к типу char:

Обратите внимание на приведение типа - " (char)b ". Значения байтов вместо перекодирования просто скопируются в char (диапазон значений 0-0xFF, а не тот, где находится кириллица). Такому копированию соответствует кодировка ISO-8859-1 (которая один в один соответствует первым 256 значениям Unicode), а значит, можно считать, что этот код просто использует её (вместо той, в которой реально закодированы символы в оригинальных данных). Если Вы попытаетесь отобразить полученное значение - на экране будут или вопросики или кракозяблы. Например, при чтении строки "АБВ" в виндовой кодировке может запросто отобразиться что-то вроде такого: "ÀÁÂ". Подобного рода код часто пишут программисты на западе - с английскими буквами работает, и ладно. Исправить такой код легко - надо просто заменить StringBuffer на ByteArrayOutputStream:

8-ми битовые кодировки русских букв

Вот основные 8-ми битовые кодировки русских букв, получившие распространение:

Помимо основного названия можно использовать синонимы. Набор их может отличаться в разных версиях JDK. Вот список от JDK 1.3.1:

  • Cp1251:
    • Windows-1251
    • IBM866
    • IBM-866
    • CP866
    • CSIBM866
    • KOI8-R
    • CSKOI8R
    • ISO8859-5
    • ISO-8859-5
    • ISO_8859-5
    • ISO_8859-5:1988
    • ISO-IR-144
    • 8859_5
    • Cyrillic
    • CSISOLatinCyrillic
    • IBM915
    • IBM-915
    • Cp915

    Причём синонимы, в отличии от основного имени нечувствительны к регистру символов - такова особенность реализации.

    Стоит отметить, что эти кодировки на некоторых JVM могут отсутствовать. Например, с сайта Sun можно скачать две разные версии JRE - US и International. В US версии присутствует только минимум - ISO-8859-1, ASCII, Cp1252, UTF8, UTF16 и несколько вариаций двухбайтового Unicode. Всё прочее есть только в International варианте. Иногда из-за этого можно нарваться на грабли с запуском программы, даже если ей не нужны русские буквы. Типичная ошибка, возникающая при этом:

    Возникает она, как не трудно догадаться, из-за того, что JVM, исходя из русских региональных настроек пытается установить кодировку по умолчанию в Cp1251, но, т.к. класс поддержки таковой отсутствует в US версии, закономерно обламывается.

    Файлы и потоки данных

    Так же как и байты концептуально отделены от символов, в Java различаются потоки байтов и потоки символов. Работу с байтами представляют классы, которые прямо или косвенно наследуют классы InputStream или OutputStream (плюс класс-уникум RandomAccessFile). Работу с символами представляет сладкая парочка классов Reader/Writer (и их наследники, разумеется).

    Для чтения/записи не преобразованных байтов используются потоки байтов. Если известно, что байты представляют собой только символы в некоторой кодировке, можно использовать специальные классы-преобразователи InputStreamReader и OutputStreamWriter, чтобы получить поток символов и работать непосредственно с ним. Обычно это удобно в случае обычных текстовых файлов или при работе с многими сетевыми протоколами Internet. Кодировка символов при этом указывается в конструкторе класса-преобразователя. Пример:

    Если в потоке могут присутствовать данные в разных кодировках или же символы перемешаны с прочими двоичными данными, то лучше читать и записывать массивы байтов (byte), а для перекодировки использовать уже упомянутые методы класса String. Пример:

    Консоль в Java традиционно представлена потоками, но, к сожалению, не символов, а байтов. Дело в том, что потоки символов появились только в JDK 1.1 (вместе со всем механизмом кодировок), а доступ к консольному вводу/выводу проектировался ещё в JDK 1.0, что и привело к появлению уродца в виде класса PrintStream. Этот класс используется в переменных System.out и System.err, которые собственно и дают доступ к выводу на консоль. По всем признакам это поток байтов, но с кучей методов записи строк. Когда Вы записываете в него строку, внутри происходит конвертация в байты с использованием кодировки по умолчанию, что в случае виндов, как правило, неприемлемо - кодировка по умолчанию будет Cp1251 (Ansi), а для консольного окна обычно нужно использовать Cp866 (OEM). Эта ошибка была зарегистрированна ещё в 97-ом году () но Sun-овцы исправлять её вроде не торопятся. Так как метода установки кодировки в PrintStream нет, для решения этой проблемы можно подменить стандартный класс на собственный при помощи методов System.setOut() и System.setErr(). Вот, например, обычное начало в моих программах:

    Исходники класса CodepagePrintStream Вы можете найти на данном сайте: CodepagePrintStream.java .

    Если Вы сами конструируете формат данных, я рекомендую Вам использовать одну из многобайтовых кодировок. Удобнее всего обычно формат UTF8 - первые 128 значений (ASCII) в нём кодируются одним байтом, что часто может значительно уменьшить общий объём данных (не зря эта кодировка принята за основу в мире XML). Но у UTF8 есть один недостаток - кол-во требуемых байтов зависит от кода символов. Там, где это критично можно использовать один из двухбайтовых форматов Unicode (UnicodeBig или UnicodeLittle).

    Базы данных

    Для того, чтобы прочитать корректно символы из БД, обычно достаточно указать JDBC-драйверу на нужную кодировку символов в БД. Как именно - это зависит от конкретного драйвера. Сейчас уже многие драйвера поддерживают подобную настройку, в отличии от недавнего прошлого. Далее приведены несколько известных мне примеров.

    Мост JDBC-ODBC

    Это один из самых часто используемых драйверов. Мост из JDK 1.2 и старше можно легко настроить на нужную кодировку. Это делается добавлением дополнительного свойства charSet в набор параметров, передаваемых для открытия соединения с базой. По умолчанию используется file.encoding . Делается это примерно так:

    Драйвер JDBC-OCI от Oracle 8.0.5 под Linux

    При получении данных из БД, данный драйвер определяет "свою" кодировку при помощи переменной окружения NLS_LANG. Если эта переменная не найдена, то он считает что кодировка - ISO-8859-1. Весь фокус в том, что NLS_LANG должна быть именно переменной окружения (устанавливаемой командой set), а не системное свойство Java (типа file.encoding). В случае использования драйвера внутри servlet engine Apache+Jserv, переменную окружения можно задать в файле jserv.properties:

    Драйвер JDBC для работы с DBF (zyh.sql.dbf.DBFDriver)

    Данный драйвер только недавно научился работать с русскими буквами. Хоть он и сообщает по getPropertyInfo() что он понимает свойство charSet - это фикция (по крайней мере в версии от 30.07.2001). Реально же настроить кодировку можно установкой свойства CODEPAGEID. Для русских символов там доступны два значения - "66" для Cp866 и "C9" для Cp1251. Пример:

    MySQL (org.gjt.mm.mysql.Driver)

    С этим драйвером тоже всё довольно просто:

    InterBase (interbase.interclient.Driver)

    Однако не забудьте при создании БД и таблиц указать кодировку символов. Для русского языка можно использовать значения "UNICODE_FSS" или "WIN1251". Пример:

    В версии 2.01 InterClient присутствует ошибка - классы ресурсов с сообщениями для русского языка там неправильно скомпилированы. Скорей всего разработчики просто забыли указать кодировку исходников при компиляции. Есть два пути исправления этой ошибки:

    • Использовать interclient-core.jar вместо interclient.jar. При этом русских ресурсов просто не будет, и автоматом подхватятся английские.
    • Перекомпилировать файлы в нормальный Unicode. Разбор class-файлов - дело неблагодарное, поэтому лучше воспользоваться JAD-ом. К сожалению JAD, если встречает символы из набора ISO-8859-1, выводит их в 8-иричной кодировке, так что воспользоваться стандартным перекодировщиком native2ascii не удастся - придётся написать свой (программа Decode). Если Вам не хочется заморачиваться с этими проблемами - можете просто взять готовый файл с ресурсами (пропатченый jar с драйвером - interclient.jar , отдельные классы ресурсов - interclient-rus.jar).

    Но даже настроив JDBC-драйвер на нужную кодировку в некоторых случаях можно нарваться на неприятности. Например, при попытке использования новых замечательных скролируемых курсоров стандарта JDBC 2 в мосте JDBC-ODBC из JDK 1.3.x довольно быстро можно обнаружить, что русские буквы там просто не работают (метод updateString()).

    С этой ошибкой связанна небольшая история. Когда я впервые её обнаружил (под JDK 1.3 rc2), я зарегистрил её на BugParade (). Когда вышла первая бета JDK 1.3.1, эту ошибку пометили как пофиксеную. Обрадованный, я скачал эту бету, запустил тест - не работает. Написал Sun-овцам об этом - в ответ мне написали, что фикс будет включён в последующие релизы. Ну ладно, подумал я, подождём. Время шло, вышел релиз 1.3.1, а потом и бета 1.4. Наконец я нашёл время проверить - опять не работает. Мать, мать, мать. - привычно откликнулось эхо. После гневного письма в Sun они завели новую ошибку (), которую отдали на растерзание индийскому филиалу. Индусы пошаманили над кодом, и заявили, что в 1.4 beta3 всё исправлено. Скачал я эту версию, запустил под ним тестовый пример, вот результат - . Как оказалось, та beta3, которую раздают на сайте (build 84) это не та beta3, куда был включён окончательный фикс (build 87). Теперь обещают, что исправление войдёт в 1.4 rc1. Ну, в общем, Вы понимаете:-)

    Русские буквы в исходниках Java-программ

    Как уже упоминалось, при выполнении программы используется Unicode. Исходные же файлы пишутся в обычных редакторах. Я пользуюсь Far-ом, у Вас, наверняка есть свой любимый редактор. Эти редакторы сохраняют файлы в 8-битовом формате, а значит, что к этим файлам также применимы рассуждения, аналогичные приведённым выше. Разные версии компиляторов немного по разному выполняют преобразование символов. В ранних версиях JDK 1.1.x используется настройка file.encoding , которую можно поменять при помощи нестандартной опции -J. В более новых (как сообщил Денис Кокарев - начиная с 1.1.4) был введён дополнительный параметр -encoding, при помощи которого можно указать используемую кодировку. В скомпилированных классах строки представлены в виде Unicode (точнее в модифицированном варианте формата UTF8), так что самое интересное происходит при компиляции. Поэтому, самое главное - выяснить, в какой кодировке у Вас исходники и указать правильное значение при компиляции. По умолчанию будет использован всё тот же пресловутый file.encoding . Пример вызова компилятора:

    Кроме использования этой настройки есть ещё один метод - указывать буквы в формате "uXXXX", где указывается код символа. Этот метод работает со всеми версиями, а для получения этих кодов можно использовать стандартную утилиту .

    Если Вы пользуетесь каким-либо IDE, то у него могут быть свои глюки. Зачастую эти IDE пользуются для чтения/сохранения исходников кодировку по умолчанию - так что обращайте внимание на региональные настройки своей ОС. Кроме этого могут быть и явные ошибки - например довольно неплохая IDE-шка CodeGuide плохо переваривает заглавную русскую букву "Т". Встроенный анализатор кода принимает эту букву за двойную кавычку, что приводит к тому, что корректный код воспринимается как ошибочный. Бороться с этим можно (заменой буквы "Т" на её код "u0422"), но неприятно. Судя по всему где-то внутри парсера применяется явное преобразование символов в байты (типа: byte b = (byte)c), поэтому вместо кода 0x0422 (код буквы "Т") получается код 0x22 (код двойной кавычки).

    Другая проблема встречается у JBuilder, но она больше связанна с эргономикой. Дело в том, что в JDK 1.3.0, под которым по умолчанию работает JBuilder, имеется бага (), из-за которой вновь создаваемые окна GUI при активизации автоматически включают раскладку клавиатуры в зависимости от региональных настроек ОС. Т.е. если у Вас русские региональные настройки, то он будет постоянно пытаться переключиться на русскую раскладку, что при написании программ жутко мешается. На сайте JBuilder.ru есть парочка патчиков которые меняют текущую локаль в JVM на Locale.US, но самый лучший способ - перейти на JDK 1.3.1, в котором данная бага пофиксена.

    Начинающие пользователи JBuilder могут также встретиться с такой проблемой - русские буквы сохраняются в виде кодов "uXXXX". Чтобы этого избежать, надо в диалоге Default Project Properties, закладка General, в поле Encoding поменять Default на Cp1251.

    Если Вы используете для компиляции не стандартный javac, а другой компилятор - обратите внимание на то, как он выполняет преобразование символов. Например, некоторые версии IBM-овского компилятора jikes не понимают, что бывают кодировки, отличные от ISO-8859-1:-). Существуют версии, пропатченые на этот счёт, но часто там тоже зашивается некоторая кодировка - нет такого удобства, как в javac.

    JavaDoc

    Для генерации HTML-документации по исходникам используется утилита javadoc, входящая в стандартную поставку JDK. Для указания кодировок у неё есть аж 3 параметра:

    • -encoding - эта настройка задаёт кодировку исходников. По умолчанию используется file.encoding.
    • -docencoding - эта настройка задаёт кодировку генерируемых HTML-файлов. По умолчанию используется file.encoding.
    • -charset - эта настройка задаёт кодировку, которая будет записываться в заголовки генерируемых HTML-файлов ( ). Очевидно, что она должна совпадать с предыдущей настройкой. Если данная настройка опущена, то тег meta добавляться не будет.

    Русские буквы в файлах properties

    Для чтения файлов properties используются методы загрузки ресурсов, которые работают специфичным образом. Собственно для чтения используется метод Properties.load, который не использует file.encoding (там в исходниках жёстко указана кодировка ISO-8859-1), поэтому единственный способ указать русские буквы - использовать формат "uXXXX" и утилиту .

    Метод Properties.save работает по разному в версиях JDK 1.1 и 1.2. В версиях 1.1 он просто отбрасывал старший байт, поэтому правильно работал только с англицкими буквами. В 1.2 делается обратное преобразование в "uXXXX", так что он работает зеркально к методу load.

    Если файлы properties у Вас загружаются не как ресурсы, а как обычные файлы конфигурации, и Вас не устраивает такое поведение - выход один, написать собственный загрузчик.

    Русские буквы в Servlet-ах.

    Ну, для чего эти самые Servlet-ы нужны, я думаю, Вы в курсе. Если нет - то лучше сначала прочитать документацию. Здесь же рассказывается только об особенностях работы с русскими буквами.

    Так в чём же особенности? Когда Servlet посылает ответ клиенту, есть два способа послать этот ответ - через OutputStream (метод getOutputStream()) или через PrintWriter (метод getWriter()). В первом случае Вы записываете массивы байтов, поэтому применимы вышеописанные методы записи в потоки. В случае же PrintWriter, он использует установленную кодировку. В любом случае необходимо правильно указать используемую кодировку при вызове метода setContentType(), для того, чтобы было правильное преобразование символов на стороне сервера. Это указание должно быть сделано перед вызовом getWriter() или перед первой записью в OutputStream. Пример:

    Это по поводу отдачи ответов клиенту. Со входными параметрами, к сожалению не так просто. Входные параметры кодируются броузером побайтно в соответствии с MIME-типом "application/x-www-form-urlencoded". Как рассказал Алексей Менделев русские буквы броузеры кодируют, используя текущую установленную кодировку. Ну и, разумеется, ничего о ней не сообщают. Соответственно, например, в JSDK версий от 2.0 до 2.2 это никак не проверяется, а то, что за кодировка будет использована для преобразования - зависит от используемого engine. Начиная со спецификации 2.3 появилась возможность устанавливать кодировку для javax.servlet.ServletRequest - метод setCharacterEncoding(). Эту спецификацию уже поддерживают последние версии Resin и Tomcat .

    Таким образом, если Вам повезло, и у Вас стоит сервер с поддержкой Servlet 2.3, то всё довольно просто:

    В применении метода request.setCharacterEncoding() есть одна существенная тонкость - он должен быть применен до первого обращения к запросу за данными (например request.getParameter()). Если Вы используете фильтры, которые обрабатывают запрос до того как он приходит в сервлет, есть ненулевая вероятность того, что в одном из фильтров может произойти чтение какого-нибудь параметра из запроса (например для авторизации) и request.setCharacterEncoding() в сервлете не сработает.

    Потому идеологически более правильно написать фильтр, устанавливающий кодировку запроса. При этом он должен стоять первым в цепочке фильтров в web.xml.

    Пример такого фильтра:

    И его конфигурации в web.xml:

    Если же Вам не повезло, и у Вас более старая версия - для достижения результата придётся поизвращаться:

    Оригинальный способ работы с кодировками предлагает Russian Apache - расписано, как именно.

    Пакет FOP предназначен для обработки документов по стандарту XSL FO (Formating Objects). В частности он позволяет создавать PDF-документы на базе документов XML. Для преобразования из исходного XML в FO пакет FOP по умолчанию использует XSLT-процессор Xalan в паре с Xerces. Для создания итогового изображения в FOP необходимо подключить шрифты, поддерживающие русские буквы. Вот как можно проделать это для версии 0.20.1:

    1. В подкаталог conffonts (например, в c:fop-0.20.1conffonts) скопировать файлы ttf из системного каталога Windows. Для Arial normal/normal, normal/bold, italic/normal и italic/bold нужны файлы arial.ttf, arialbd.ttf, ariali.ttf и arialbi.ttf.
    2. Сгенерировать файлы описаний шрифтов (типа arial.xml). Для этого для каждого шрифта нужно выполнить команду (это для Arial normal/normal, всё в одну строку):

    Данный алгоритм прислал Алексей Тюрин , за что ему отдельное спасибо.

    Если Вы используете встроенный в FOP просмотрщик, то необходимо учесть его особенности. В частности, хотя предполагается, что надписи в нём русифицированы, на самом деле сделано это с ошибкой (в версии 0.19.0). Для загрузки надписей из файлов ресурсов в пакете org.apache.fop.viewer.resources используется собственный загрузчик (класс org.apache.fop.viewer.LoadableProperties). Кодировка чтения там жёстко зафиксирована (8859_1, как и в случае Properties.load()), однако поддержка записи вида "uXXXX" не реализована. Я сообщил об этой ошибке разработчикам, они включили её исправление в свои планы.

    Кроме всего прочего существует сайт посвящённый русификации FOP (http://www.openmechanics.org/rusfop/) Там Вы сможете найти дистрибутив FOP с уже исправленными ошибками и подключенными русскми шрифтами.

    CORBA

    Кодировки

    Когда я только начинал программировать на языке C, первой моей программой (не считая HelloWorld) была программа перекодировки текстовых файлов из основной кодировки ГОСТ-а (помните такую? :-) в альтернативную. Было это в далёком 1991-ом году. С тех пор многое изменилось, но за прошедшие 10 лет подобные программки свою актуальность, к сожалению, не потеряли. Слишком много уже накоплено данных в разнообразных кодировках и слишком много используется программ, которые умеют работать только с одной. Для русского языка существует не менее десятка различных кодировок, что делает проблему ещё более запутанной.

    Откуда же взялись все эти кодировки и для чего они нужны? Компьютеры по своей природе могут работать только с числами. Для того чтобы хранить буквы в памяти компьютера надо поставить в соответствие каждой букве некое число (примерно такой же принцип использовался и до появления компьютеров - вспомните про ту же азбуку Морзе). Причём число желательно поменьше - чем меньше двоичных разрядов будет задействовано, тем эффективнее можно будет использовать память. Вот это соответствие набора символов и чисел собственно и есть кодировка. Желание любой ценой сэкономить память, а так же разобщённость разных групп компьютерщиков и привела к нынешнему положению дел. Самым распространённым способом кодирования сейчас является использование для одного символа одного байта (8 бит), что определяет общее кол-во символов в 256. Набор первых 128 символов стандартизован (набор ASCII) и является одинаковыми во всех распространённых кодировках (те кодировки, где это не так уже практически вышли из употребления). Англицкие буковки и символы пунктуации находятся в этом диапазоне, что и определяет их поразительную живучесть в компьютерных системах:-). Другие языки находятся не в столь счастливом положении - им всем приходится ютиться в оставшихся 128 числах.

    Unicode

    В конце 80-х многие осознали необходимость создания единого стандарта на кодирование символов, что и привело к появлению Unicode. Unicode - это попытка раз и навсегда зафиксировать конкретное число за конкретным символом. Понятно, что в 256 символов тут не уложишься при всём желании. Довольно долгое время казалось, что уж 2-х то байт (65536 символов) должно хватить. Ан нет - последняя версия стандарта Unicode (3.1) определяет уже 94140 символов. Для такого кол-ва символов, наверное, уже придётся использовать 4 байта (4294967296 символов). Может быть и хватит на некоторое время. :-)

    В набор символов Unicode входят всевозможные буквы со всякими чёрточками и припендюльками, греческие, математические, иероглифы, символы псевдографики и пр. и пр. В том числе и так любимые нами символы кириллицы (диапазон значений 0x0400-0x04ff). Так что с этой стороны никакой дискриминации нет.

    Если Вам интересны конкретные кода символов, для их просмотра удобно использовать программу "Таблица символов" из WinNT. Вот, например, диапазон кириллицы:

    Если у Вас другая OS или Вас интересует официальное толкование, то полную раскладку символов (charts) можно найти на официальном сайте Unicode (http://www.unicode.org/charts/web.html).

    Типы char и byte

    В Java для символов выделен отдельный тип данных char размером в 2 байта. Это часто порождает путаницу в умах начинающих (особенно если они раньше программировали на других языках, например на C/C++). Дело в том, что в большинстве других языков для обработки символов используются типы данных размером в 1 байт. Например, в C/C++ тип char в большинстве случаев используется как для обработки символов, так и для обработки байтов - там нет разделения. В Java для байтов имеется свой тип - тип byte . Таким образом C-ишному char соответствует Java-вский byte , а Java-вскому char из мира C ближе всего тип wchar_t . Надо чётко разделять понятия символов и байтов - иначе непонимание и проблемы гарантированны.

    Java практически с самого своего рождения использует для кодирования символов стандарт Unicode. Библиотечные функции Java ожидают увидеть в переменных типа char символы, представленные кодами Unicode. В принципе, Вы, конечно, можете запихнуть туда что угодно - цифры есть цифры, процессор всё стерпит, но при любой обработке библиотечные функции будут действовать исходя из предположения что им передали кодировку Unicode. Так что можно спокойно полагать, что у типа char кодировка зафиксирована. Но это внутри JVM. Когда данные читаются извне или передаются наружу, то они могут быть представлены только одним типом - типом byte . Все прочие типы конструируются из байтов в зависимости от используемого формата данных. Вот тут то на сцену и выходят кодировки - в Java это просто формат данных для передачи символов, который используется для формирования данных типа char . Для каждой кодовой страницы в библиотеке имеется по 2 класса перекодировки (ByteToChar и CharToByte). Классы эти лежат в пакете sun.io . Если, при перекодировке из char в byte не было найдено соответствующего символа, он заменяется на символ?.

    Кстати, эти файлы кодовых страниц в некоторых ранних версиях JDK 1.1 содержат ошибки, вызывающие ошибки перекодировок, а то и вообще исключения при выполнении. Например, это касается кодировки KOI8_R. Лучшее, что можно при этом сделать - сменить версию на более позднюю. Судя по Sun-овскому описанию, большинство этих проблем было решено в версии JDK 1.1.6.

    До появления версии JDK 1.4 набор доступных кодировок определялся только производителем JDK. Начиная с 1.4 появилось новое API (пакет java.nio.charset), при помощи которого Вы уже можете создать свою собственную кодировку (например поддержать редко используемую, но жутко необходимую именно Вам).

    Класс String

    В большинстве случаев для представления строк в Java используется объект типа java.lang.String . Это обычный класс, который внутри себя хранит массив символов (char), и который содержит много полезных методов для манипуляции символами. Самые интересные - это конструкторы, имеющие первым параметром массив байтов (byte) и методы getBytes() . При помощи этих методов Вы можете выполнять преобразования из массива байтов в строки и обратно. Для того, чтобы указать какую кодировку при этом использовать у этих методов есть строковый параметр, который задаёт её имя. Вот, например, как можно выполнить перекодировку байтов из КОИ-8 в Windows-1251:

    Список 8-ми битовых кодировок, доступных в современных JDK и поддерживающих русские буквы Вы можете найти ниже, в разделе .

    Так как кодировка - это формат данных для символов, кроме знакомых 8-ми битовых кодировок в Java также на равных присутствуют и многобайтовые кодировки. К таким относятся UTF-8, UTF-16, Unicode и пр. Например вот так можно получить байты в формате UnicodeLittleUnmarked (16-ти битовое кодирование Unicode, младший байт первый, без признака порядка байтов):

    При подобных преобразованиях легко ошибиться - если кодировка байтовых данных не соотвествуют указанному параметру при преобразовании из byte в char , то перекодирование будет выполнено неправильно. Иногда после этого можно вытащить правильные символы, но чаще всего часть данных будет безвозвратно потеряна.

    В реальной программе явно указывать кодовую страницу не всегда удобно (хотя более надёжно). Для этого была введена кодировка по умолчанию. По умолчанию она зависит от системы и её настроек (для русских виндов принята кодировка Cp1251), и в старых JDK её можно изменить установкой системного свойства file.encoding . В JDK 1.3 изменение этой настройки иногда срабатывает, иногда - нет. Вызвано это следующим: первоначально file.encoding ставится по региональным настройкам компьютера. Ссылка на кодировку по умолчанию запоминается в нутрях при первом преобразовании. При этом используется file.encoding, но это преобразование происходит ещё до использования аргументов запуска JVM (собсно, при их разборе). Вообще-то, как утверждают в Sun, это свойство отражает системную кодировку, и она не должна изменяться в командной строке (см., например, комментарии к BugID ) Тем не менее в JDK 1.4 Beta 2 смена этой настройки опять начала оказывать эффект. Что это, сознательное изменение или побочный эффект, который может опять исчезнуть - Sun-овцы ясного ответа пока не дали.

    Эта кодировка используется тогда, когда явно не указанно название страницы. Об этом надо всегда помнить - Java не будет пытаться предсказать кодировку байтов, которые Вы передаёте для создания строки String (так же она не сможет прочитать Ваши мысли по этому поводу:-). Она просто использует текущую кодировку по умолчанию. Т.к. эта настройка одна на все преобразования, иногда можно наткнуться на неприятности.

    Для преобразования из байтов в символы и обратно следует пользовать только этими методами. Простое приведение типа использовать в большинстве случаев нельзя - кодировка символов при этом не будет учитываться. Например, одной из самых распространённых ошибок является чтение данных побайтно при помощи метода read() из InputStream, а затем приведение полученного значения к типу char:

    Обратите внимание на приведение типа - " (char)b ". Значения байтов вместо перекодирования просто скопируются в char (диапазон значений 0-0xFF, а не тот, где находится кириллица). Такому копированию соответствует кодировка ISO-8859-1 (которая один в один соответствует первым 256 значениям Unicode), а значит, можно считать, что этот код просто использует её (вместо той, в которой реально закодированы символы в оригинальных данных). Если Вы попытаетесь отобразить полученное значение - на экране будут или вопросики или кракозяблы. Например, при чтении строки "АБВ" в виндовой кодировке может запросто отобразиться что-то вроде такого: "ÀÁÂ". Подобного рода код часто пишут программисты на западе - с английскими буквами работает, и ладно. Исправить такой код легко - надо просто заменить StringBuffer на ByteArrayOutputStream:

    8-ми битовые кодировки русских букв

    Вот основные 8-ми битовые кодировки русских букв, получившие распространение:

    Помимо основного названия можно использовать синонимы. Набор их может отличаться в разных версиях JDK. Вот список от JDK 1.3.1:

    • Cp1251:
      • Windows-1251
      • IBM866
      • IBM-866
      • CP866
      • CSIBM866
      • KOI8-R
      • CSKOI8R
      • ISO8859-5
      • ISO-8859-5
      • ISO_8859-5
      • ISO_8859-5:1988
      • ISO-IR-144
      • 8859_5
      • Cyrillic
      • CSISOLatinCyrillic
      • IBM915
      • IBM-915
      • Cp915

      Причём синонимы, в отличии от основного имени нечувствительны к регистру символов - такова особенность реализации.

      Стоит отметить, что эти кодировки на некоторых JVM могут отсутствовать. Например, с сайта Sun можно скачать две разные версии JRE - US и International. В US версии присутствует только минимум - ISO-8859-1, ASCII, Cp1252, UTF8, UTF16 и несколько вариаций двухбайтового Unicode. Всё прочее есть только в International варианте. Иногда из-за этого можно нарваться на грабли с запуском программы, даже если ей не нужны русские буквы. Типичная ошибка, возникающая при этом:

      Возникает она, как не трудно догадаться, из-за того, что JVM, исходя из русских региональных настроек пытается установить кодировку по умолчанию в Cp1251, но, т.к. класс поддержки таковой отсутствует в US версии, закономерно обламывается.

      Файлы и потоки данных

      Так же как и байты концептуально отделены от символов, в Java различаются потоки байтов и потоки символов. Работу с байтами представляют классы, которые прямо или косвенно наследуют классы InputStream или OutputStream (плюс класс-уникум RandomAccessFile). Работу с символами представляет сладкая парочка классов Reader/Writer (и их наследники, разумеется).

      Для чтения/записи не преобразованных байтов используются потоки байтов. Если известно, что байты представляют собой только символы в некоторой кодировке, можно использовать специальные классы-преобразователи InputStreamReader и OutputStreamWriter, чтобы получить поток символов и работать непосредственно с ним. Обычно это удобно в случае обычных текстовых файлов или при работе с многими сетевыми протоколами Internet. Кодировка символов при этом указывается в конструкторе класса-преобразователя. Пример:

      Если в потоке могут присутствовать данные в разных кодировках или же символы перемешаны с прочими двоичными данными, то лучше читать и записывать массивы байтов (byte), а для перекодировки использовать уже упомянутые методы класса String. Пример:

      Консоль в Java традиционно представлена потоками, но, к сожалению, не символов, а байтов. Дело в том, что потоки символов появились только в JDK 1.1 (вместе со всем механизмом кодировок), а доступ к консольному вводу/выводу проектировался ещё в JDK 1.0, что и привело к появлению уродца в виде класса PrintStream. Этот класс используется в переменных System.out и System.err, которые собственно и дают доступ к выводу на консоль. По всем признакам это поток байтов, но с кучей методов записи строк. Когда Вы записываете в него строку, внутри происходит конвертация в байты с использованием кодировки по умолчанию, что в случае виндов, как правило, неприемлемо - кодировка по умолчанию будет Cp1251 (Ansi), а для консольного окна обычно нужно использовать Cp866 (OEM). Эта ошибка была зарегистрированна ещё в 97-ом году () но Sun-овцы исправлять её вроде не торопятся. Так как метода установки кодировки в PrintStream нет, для решения этой проблемы можно подменить стандартный класс на собственный при помощи методов System.setOut() и System.setErr(). Вот, например, обычное начало в моих программах:

      Исходники класса CodepagePrintStream Вы можете найти на данном сайте: CodepagePrintStream.java .

      Если Вы сами конструируете формат данных, я рекомендую Вам использовать одну из многобайтовых кодировок. Удобнее всего обычно формат UTF8 - первые 128 значений (ASCII) в нём кодируются одним байтом, что часто может значительно уменьшить общий объём данных (не зря эта кодировка принята за основу в мире XML). Но у UTF8 есть один недостаток - кол-во требуемых байтов зависит от кода символов. Там, где это критично можно использовать один из двухбайтовых форматов Unicode (UnicodeBig или UnicodeLittle).

      Базы данных

      Для того, чтобы прочитать корректно символы из БД, обычно достаточно указать JDBC-драйверу на нужную кодировку символов в БД. Как именно - это зависит от конкретного драйвера. Сейчас уже многие драйвера поддерживают подобную настройку, в отличии от недавнего прошлого. Далее приведены несколько известных мне примеров.

      Мост JDBC-ODBC

      Это один из самых часто используемых драйверов. Мост из JDK 1.2 и старше можно легко настроить на нужную кодировку. Это делается добавлением дополнительного свойства charSet в набор параметров, передаваемых для открытия соединения с базой. По умолчанию используется file.encoding . Делается это примерно так:

      Драйвер JDBC-OCI от Oracle 8.0.5 под Linux

      При получении данных из БД, данный драйвер определяет "свою" кодировку при помощи переменной окружения NLS_LANG. Если эта переменная не найдена, то он считает что кодировка - ISO-8859-1. Весь фокус в том, что NLS_LANG должна быть именно переменной окружения (устанавливаемой командой set), а не системное свойство Java (типа file.encoding). В случае использования драйвера внутри servlet engine Apache+Jserv, переменную окружения можно задать в файле jserv.properties:

      Драйвер JDBC для работы с DBF (zyh.sql.dbf.DBFDriver)

      Данный драйвер только недавно научился работать с русскими буквами. Хоть он и сообщает по getPropertyInfo() что он понимает свойство charSet - это фикция (по крайней мере в версии от 30.07.2001). Реально же настроить кодировку можно установкой свойства CODEPAGEID. Для русских символов там доступны два значения - "66" для Cp866 и "C9" для Cp1251. Пример:

      MySQL (org.gjt.mm.mysql.Driver)

      С этим драйвером тоже всё довольно просто:

      InterBase (interbase.interclient.Driver)

      Однако не забудьте при создании БД и таблиц указать кодировку символов. Для русского языка можно использовать значения "UNICODE_FSS" или "WIN1251". Пример:

      В версии 2.01 InterClient присутствует ошибка - классы ресурсов с сообщениями для русского языка там неправильно скомпилированы. Скорей всего разработчики просто забыли указать кодировку исходников при компиляции. Есть два пути исправления этой ошибки:

      • Использовать interclient-core.jar вместо interclient.jar. При этом русских ресурсов просто не будет, и автоматом подхватятся английские.
      • Перекомпилировать файлы в нормальный Unicode. Разбор class-файлов - дело неблагодарное, поэтому лучше воспользоваться JAD-ом. К сожалению JAD, если встречает символы из набора ISO-8859-1, выводит их в 8-иричной кодировке, так что воспользоваться стандартным перекодировщиком native2ascii не удастся - придётся написать свой (программа Decode). Если Вам не хочется заморачиваться с этими проблемами - можете просто взять готовый файл с ресурсами (пропатченый jar с драйвером - interclient.jar , отдельные классы ресурсов - interclient-rus.jar).

      Но даже настроив JDBC-драйвер на нужную кодировку в некоторых случаях можно нарваться на неприятности. Например, при попытке использования новых замечательных скролируемых курсоров стандарта JDBC 2 в мосте JDBC-ODBC из JDK 1.3.x довольно быстро можно обнаружить, что русские буквы там просто не работают (метод updateString()).

      С этой ошибкой связанна небольшая история. Когда я впервые её обнаружил (под JDK 1.3 rc2), я зарегистрил её на BugParade (). Когда вышла первая бета JDK 1.3.1, эту ошибку пометили как пофиксеную. Обрадованный, я скачал эту бету, запустил тест - не работает. Написал Sun-овцам об этом - в ответ мне написали, что фикс будет включён в последующие релизы. Ну ладно, подумал я, подождём. Время шло, вышел релиз 1.3.1, а потом и бета 1.4. Наконец я нашёл время проверить - опять не работает. Мать, мать, мать. - привычно откликнулось эхо. После гневного письма в Sun они завели новую ошибку (), которую отдали на растерзание индийскому филиалу. Индусы пошаманили над кодом, и заявили, что в 1.4 beta3 всё исправлено. Скачал я эту версию, запустил под ним тестовый пример, вот результат - . Как оказалось, та beta3, которую раздают на сайте (build 84) это не та beta3, куда был включён окончательный фикс (build 87). Теперь обещают, что исправление войдёт в 1.4 rc1. Ну, в общем, Вы понимаете:-)

      Русские буквы в исходниках Java-программ

      Как уже упоминалось, при выполнении программы используется Unicode. Исходные же файлы пишутся в обычных редакторах. Я пользуюсь Far-ом, у Вас, наверняка есть свой любимый редактор. Эти редакторы сохраняют файлы в 8-битовом формате, а значит, что к этим файлам также применимы рассуждения, аналогичные приведённым выше. Разные версии компиляторов немного по разному выполняют преобразование символов. В ранних версиях JDK 1.1.x используется настройка file.encoding , которую можно поменять при помощи нестандартной опции -J. В более новых (как сообщил Денис Кокарев - начиная с 1.1.4) был введён дополнительный параметр -encoding, при помощи которого можно указать используемую кодировку. В скомпилированных классах строки представлены в виде Unicode (точнее в модифицированном варианте формата UTF8), так что самое интересное происходит при компиляции. Поэтому, самое главное - выяснить, в какой кодировке у Вас исходники и указать правильное значение при компиляции. По умолчанию будет использован всё тот же пресловутый file.encoding . Пример вызова компилятора:

      Кроме использования этой настройки есть ещё один метод - указывать буквы в формате "\uXXXX", где указывается код символа. Этот метод работает со всеми версиями, а для получения этих кодов можно использовать стандартную утилиту .

      Если Вы пользуетесь каким-либо IDE, то у него могут быть свои глюки. Зачастую эти IDE пользуются для чтения/сохранения исходников кодировку по умолчанию - так что обращайте внимание на региональные настройки своей ОС. Кроме этого могут быть и явные ошибки - например довольно неплохая IDE-шка CodeGuide плохо переваривает заглавную русскую букву "Т". Встроенный анализатор кода принимает эту букву за двойную кавычку, что приводит к тому, что корректный код воспринимается как ошибочный. Бороться с этим можно (заменой буквы "Т" на её код "\u0422"), но неприятно. Судя по всему где-то внутри парсера применяется явное преобразование символов в байты (типа: byte b = (byte)c), поэтому вместо кода 0x0422 (код буквы "Т") получается код 0x22 (код двойной кавычки).

      Другая проблема встречается у JBuilder, но она больше связанна с эргономикой. Дело в том, что в JDK 1.3.0, под которым по умолчанию работает JBuilder, имеется бага (), из-за которой вновь создаваемые окна GUI при активизации автоматически включают раскладку клавиатуры в зависимости от региональных настроек ОС. Т.е. если у Вас русские региональные настройки, то он будет постоянно пытаться переключиться на русскую раскладку, что при написании программ жутко мешается. На сайте JBuilder.ru есть парочка патчиков которые меняют текущую локаль в JVM на Locale.US, но самый лучший способ - перейти на JDK 1.3.1, в котором данная бага пофиксена.

      Начинающие пользователи JBuilder могут также встретиться с такой проблемой - русские буквы сохраняются в виде кодов "\uXXXX". Чтобы этого избежать, надо в диалоге Default Project Properties, закладка General, в поле Encoding поменять Default на Cp1251.

      Если Вы используете для компиляции не стандартный javac, а другой компилятор - обратите внимание на то, как он выполняет преобразование символов. Например, некоторые версии IBM-овского компилятора jikes не понимают, что бывают кодировки, отличные от ISO-8859-1:-). Существуют версии, пропатченые на этот счёт, но часто там тоже зашивается некоторая кодировка - нет такого удобства, как в javac.

      JavaDoc

      Для генерации HTML-документации по исходникам используется утилита javadoc, входящая в стандартную поставку JDK. Для указания кодировок у неё есть аж 3 параметра:

      • -encoding - эта настройка задаёт кодировку исходников. По умолчанию используется file.encoding.
      • -docencoding - эта настройка задаёт кодировку генерируемых HTML-файлов. По умолчанию используется file.encoding.
      • -charset - эта настройка задаёт кодировку, которая будет записываться в заголовки генерируемых HTML-файлов ( ). Очевидно, что она должна совпадать с предыдущей настройкой. Если данная настройка опущена, то тег meta добавляться не будет.

      Русские буквы в файлах properties

      Для чтения файлов properties используются методы загрузки ресурсов, которые работают специфичным образом. Собственно для чтения используется метод Properties.load, который не использует file.encoding (там в исходниках жёстко указана кодировка ISO-8859-1), поэтому единственный способ указать русские буквы - использовать формат "\uXXXX" и утилиту .

      Метод Properties.save работает по разному в версиях JDK 1.1 и 1.2. В версиях 1.1 он просто отбрасывал старший байт, поэтому правильно работал только с англицкими буквами. В 1.2 делается обратное преобразование в "\uXXXX", так что он работает зеркально к методу load.

      Если файлы properties у Вас загружаются не как ресурсы, а как обычные файлы конфигурации, и Вас не устраивает такое поведение - выход один, написать собственный загрузчик.

      Русские буквы в Servlet-ах.

      Ну, для чего эти самые Servlet-ы нужны, я думаю, Вы в курсе. Если нет - то лучше сначала прочитать документацию. Здесь же рассказывается только об особенностях работы с русскими буквами.

      Так в чём же особенности? Когда Servlet посылает ответ клиенту, есть два способа послать этот ответ - через OutputStream (метод getOutputStream()) или через PrintWriter (метод getWriter()). В первом случае Вы записываете массивы байтов, поэтому применимы вышеописанные методы записи в потоки. В случае же PrintWriter, он использует установленную кодировку. В любом случае необходимо правильно указать используемую кодировку при вызове метода setContentType(), для того, чтобы было правильное преобразование символов на стороне сервера. Это указание должно быть сделано перед вызовом getWriter() или перед первой записью в OutputStream. Пример:

      Это по поводу отдачи ответов клиенту. Со входными параметрами, к сожалению не так просто. Входные параметры кодируются броузером побайтно в соответствии с MIME-типом "application/x-www-form-urlencoded". Как рассказал Алексей Менделев русские буквы броузеры кодируют, используя текущую установленную кодировку. Ну и, разумеется, ничего о ней не сообщают. Соответственно, например, в JSDK версий от 2.0 до 2.2 это никак не проверяется, а то, что за кодировка будет использована для преобразования - зависит от используемого engine. Начиная со спецификации 2.3 появилась возможность устанавливать кодировку для javax.servlet.ServletRequest - метод setCharacterEncoding(). Эту спецификацию уже поддерживают последние версии Resin и Tomcat .

      Таким образом, если Вам повезло, и у Вас стоит сервер с поддержкой Servlet 2.3, то всё довольно просто:

      В применении метода request.setCharacterEncoding() есть одна существенная тонкость - он должен быть применен до первого обращения к запросу за данными (например request.getParameter()). Если Вы используете фильтры, которые обрабатывают запрос до того как он приходит в сервлет, есть ненулевая вероятность того, что в одном из фильтров может произойти чтение какого-нибудь параметра из запроса (например для авторизации) и request.setCharacterEncoding() в сервлете не сработает.

      Потому идеологически более правильно написать фильтр, устанавливающий кодировку запроса. При этом он должен стоять первым в цепочке фильтров в web.xml.

      Пример такого фильтра:

      И его конфигурации в web.xml:

      Если же Вам не повезло, и у Вас более старая версия - для достижения результата придётся поизвращаться:

      Оригинальный способ работы с кодировками предлагает Russian Apache - расписано, как именно.

      Если программа не работает нигде - значит проблема только в ней и в Ваших руках. Внимательно перечитайте всё, что было написано выше, и ищите. Если же проблема проявляется только в конкретном окружении - значит дело, возможно в настройках. Где именно - зависит от того, какой графической библиотекой Вы пользуетесь. Если AWT - помочь может правильная настройка файла font.properties.ru. Пример корректного файла можно взять из Java 2. Если у Вас нет этой версии, можете скачать его с данного сайта: версия для Windows , версия для Linux (см. также ниже). Этот файл задаёт используемые шрифты и кодовые страницы. Если у Вас установлена русская версия OS - просто добавьте этот файл туда, где лежит файл font.properties. Если же это англицкая версия, то нужно, или переписать этот файл вместо font.properties или дополнительно сменить текущие региональные настройки на русские. Иногда может сработать настройка -Duser.language=ru , но чаще - нет. Тут примерно те же проблемы, что и с file.encoding - сработает или нет, зависит от JDK (см. ошибку за номером ).

      С библиотекой Swing всё проще - в ней всё рисуется через подсистему Java2D. Надписи в стандартных диалогах (JOptionPane, JFileChooser, JColorChooser) переделать на русский очень просто - достаточно лишь создать несколько файлов ресурсов. Я это уже проделал, так что можете просто взять готовый файл и добавить его в lib\ext или в CLASSPATH. Единственная проблема, с которой я столкнулся - в версиях JDK начиная с 1.2 rc1 и по 1.3 beta, русские буквы не выводятся под Win9x при использовании стандартных шрифтов (Arial, Courier New, Times New Roman, etc.) из-за ошибки в Java2D. Ошибка весьма своеобразна - со стандартными шрифтами изображения букв отображаются не в соответствии с кодами Unicode, а по таблице Cp1251 (кодировка Ansi). Эта ошибка зарегистрирована в BugParade под номером . По умолчанию в Swing используются шрифты, задаваемые в файле font.properties.ru, так что достаточно заменить их другими - и русские буквы появляются. К сожалению, набор рабочих шрифтов небольшой - это шрифты Tahoma, Tahoma Bold и два набора шрифтов из дистрибутива JDK - Lucida Sans * и Lucida Typewriter * (пример файла font.properties.ru). Чем эти шрифты отличаются от стандартных - мне непонятно.

      Начиная с версии 1.3rc1 эта проблема уже исправлена, так что нужно просто обновить JDK. JDK 1.2 уже сильно устарел, так что я не рекомендую им пользоваться. Так же надо учесть, что с оригинальной версией Win95 поставляются шрифты, не поддерживающие Unicode - в этой ситуации можно просто скопировать шрифты из Win98 или WinNT.

      Типичные ошибки, или "куда делась буква Ш?"

      Буква Ш.

      Этот вопрос ("куда делась буква Ш?") довольно часто возникает у начинающих программистов на Java. Давайте разберёмся, куда же она действительно чаще всего девается. :-)

      Вот типичная программа а-ля HelloWorld:

      Что же произошло? Куда делась буква Ш? Весь фокус здесь в том, что произошла взаимокомпенсация двух ошибок. Текстовый редактор в Far по умолчанию создаёт файл в DOS-кодировке (Cp866). Компилятор же javac для чтения исходника использует file.encoding (если не указано иное ключиком -encoding). А в среде Windows с русскими региональными настройками кодировкой по умолчанию является Cp1251. Это первая ошибка. В результате, в скомпилированном файле Test.class символы имеют неверные кода. Вторая ошибка состоит в том, что для вывода используется стандартный PrintStream, который тоже использует настройку из file.encoding, однако консольное окно в Windows отображает символы, используя кодировку DOS. Если бы кодировка Cp1251 была взаимоодназначной, то потери данных бы не было. Но символ Ш в Cp866 имеет код 152, который в Cp1251 не определён, и поэтому отображается на Unicode-символ 0xFFFD. Когда происходит обратное преобразование из char в byte, вместо него подставляется символ "?".

      На аналогичную компенсацию можно нарваться, если прочитать символы из текстового файла при помощи java.io.FileReader, а затем вывести их на экран через System.out.println(). Если файл был записан в кодировке Cp866, то вывод будет идти верно, за исключением опять же буквы Ш.

      Прямая конверсия byte char.

      Эта ошибка является любимой у зарубежных программистов на Java. Она довольно подробно рассмотрена в начале описания. Если Вы когда-нибудь будете смотреть чужие исходники, то всегда обращайте внимание на явную конверсию типов - (byte) или (char). Довольно часто в таких местах закопаны грабли.

      Алгоритм поиска проблем с русскими буквами

      Если Вы не представляете себе где в Вашей программе может происходить потеря русских букв, то можно попробовать следующий тест. Любую программу можно рассматривать как обработчик входных данных. Русские буквы - это такие же данные, они проходят в общем случае три стадии обработки: они откуда-то читаются в память программы (вход), обрабатываются внутри программы и выводятся пользователю (выход). Для того, чтобы определить место проблем, надо попробовать вместо данных зашить в исходник такую тестовую строку: "АБВ\u0410\u0411\u0412", и попробовать её вывести. После этого смотрите, что у Вас вывелось:

      • Если Вы увидите "АБВАБВ", значит компиляция исходников и вывод у Вас работают правильно.
      • Если Вы увидите ". АБВ" (или любые другие символы кроме "АБВ" на месте первых трёх букв), значит вывод работает правильно, но вот компиляция исходников происходит неверно - скорей всего не указан ключик -encoding.
      • Если Вы увидите ". " (или любые другие символы кроме "АБВ" на месте второй тройки букв), значит вывод у Вас работает неверно.

      Настроив вывод и компиляцию уже можно легко разобраться и со входом. После настройки всей цепочки проблемы должны исчезнуть.

      Об утилите native2ascii

      Эта утилита входит в состав Sun JDK и предназначена для преобразования исходных текстов к ASCII-виду. Она читает входной файл, используя указанную кодировку, а на выходе записывает символы в формате "\uXXXX". Если указать ключик -reverse, то выполняется обратная конвертация. Эта программа очень полезна для конвертации файлов ресурсов (.properties) или для обработки исходников, если Вы предполагаете, что они могут компиляться на компьютерах с отличными от русских региональными настройками.

      Если запустить программу без параметров, она работает со стандартным входом (stdin), а не выводит подсказку по ключам, как остальные утилиты. Это приводит к тому, что многие и не догадываются о необходимости указания параметров (кроме, может быть, тех, кто нашёл в себе силы и мужество заглянуть таки в документацию:-). Между тем этой утилите для правильной работы необходимо, как минимум, указать используемую кодировку (ключик -encoding). Если этого не сделать, то будет использована кодировка по умолчанию (file.encoding), что может несколько расходится с ожидаемой. В результате, получив неверные кода букв (из-за неверной кодировки) можно потратить весьма много времени на поиск ошибок в абсолютно верном коде.

      О методе перекодировки символов

      Этот метод многие используют неправильно, наверное, не совсем понимая его суть и ограничения. Он предназначен для восстановления верных кодов букв, если они были неверно проинтерпретированы. Суть метода проста: из полученных неверных символов, используя соответствующую кодовую страницу, восстанавливается исходный массив байтов. Затем из этого массива байтов, используя уже корректную страницу, получаются нормальные кода символов. Пример:

      Проблем в использовании этого приёма может быть несколько. Например, для восстановления используется неверная страница, или же она может измениться в некоторых ситуациях. Другая проблема может быть в том, что некоторые страницы выполняют неоднозначное преобразование byte char. Смотрите, например, описание ошибки за номером .

      Поэтому пользоваться этим методом стоит только в самом крайнем случае, когда уже ничто другое не помогает, и Вы чётко себе представляете, где именно происходит неверное преобразование символов.

      Русские буквы и MS JVM

      Непонятно по каким соображениям, но в ней отсутствуют все файлы кодировок русских букв, акромя Cp1251 (наверное, они таким образом пытались уменьшить размер дистрибутива). Если Вам нужны другие кодировки, например, Cp866, то нужно добавить соответствующие классы в CLASSPATH. Причём классы от последних версий Sun JDK не подходят - у Sun-а уже давно изменилась их структура, поэтому последние версии классов с Microsoft-ом не стыкуются (у MS осталась структура от JDK 1.1.4). На сервере Microsoft, в принципе, лежит полный комплект дополнительных кодировок, но там файл размером около 3 метров, а их сервер докачку не поддерживает:-). Мне удалось таки выкачать этот файл, я его переупаковал jar-ом, можете взять его отсюда .

      Если же Вы пишете апплет, который должен работать под MS JVM и Вам потребовалось прочитать откуда-то (например из файла на сервере) байты в русской кодировке, отличной от Cp1251 (например, в Cp866), то стандартный механизм кодировок Вы уже не сможете использовать - апплетам запрещено добавлять классы в системные пакеты, коим в данном случае является пакет sun.io. Выхода тут два - или перекодировать файл на сервере в Cp1251 (или в UTF-8) или перед преобразованием из байтов в Unicode выполнять конвертацию байтов из нужной кодировки в Cp1251.

      Руссификация Java под Linux

      Скажу сразу - я с Linux не работаю, а приведённая здесь информация получена от читателей данного описания. Если Вы найдёте неточность или захотите дополнить - напишите мне.

      При кириллизации JVM в Linux существует две параллельных проблемы:

      1. Проблема вывода кириллицы в GUI-компонентах
      2. Проблема ввода кириллицы с клавиатуры (в X11)

      Проблему вывода можно решить таким способом (данный алгоритм прислал Artemy E. Kapitula):

      1. Установить в X11 нормальные шрифты ttf из Windows NT/200. Я бы рекомендовал Arial, Times New Roman, Courier New, Verdana и Tahoma - причем подключать их лучше не через сервер шрифтов, а как каталог с файлами.
      2. Добавить следующий файл font.properties.ru в каталог $JAVA_HOME/jre/lib

      Проблема ввода решается примерно таким способом (данный алгоритм прислал Михаил Иванов):

      Настройка ввода русских букв в следующей конфигурации:

      • Mandrake Linux 7.1
      • XFree86 3.3.6
      • IBM Java 1.3.0 (релизная)

      IBM Java 1.3 не дает вводить русские буквы (видны как крокозябры) при том что на лейблах и в менюхах их видно.

      использование XIM (-> xkb) в AWT. (это не есть плохо само по себе, просто с такими штуками нужно обращаться осторожно + некоторые прилады xkb не любят).

      Настроить xkb (и локаль (xkb без локали НЕ РАБОТАЕТ))

        выставляется локаль (где-нибудь типа в /etc/profile или в

      Теперь русские буквы должны вводиться и выводиться в свинге без проблем.

      Исторически сложилось так, что для представления печатных символов (кодирования текста) в первых ЭВМ отвели 7 бит. 2 7 =128. Этого количества вполне хватало для кодирования всех строчных и прописных букв латинского алфавита, десяти цифр и различных знаков и скобок. Именно такой, 7-битной, является таблица символов ASCII (американский стандартный код для обмена информацией), подробную информацию о которой вы можете получить при помощи команды man ascii операционной системы Linux.

      Когда возникла необходимость кодировать национальные алфавиты, то 128 символов стало недостаточно. Было решено перейти на кодирование с помощью 8 бит (т. е. одного байта). В результате количество символов, которые можно закодировать таким образом стало равно 2 8 =256. При этом символы национальных алфавитов располагались во второй половине кодовой таблицы, т. е. содержали единицу в старшем разряде байта, отведенного для кодирования символа. Так появился стандарт ISO 8859, содержащий множество кодировок для наиболее распространенных языков.

      Среди них была и одна из первых таблиц для кодировки русских букв -- ISO 8859-5 (воспользуйтесь командой man iso_8859_1 для получения кодов русских букв в этой таблице).

      Задачи передачи текстовой информации по сети вынудили разработать еще одну кодировку для русских букв, названную Koi8-R (код отображения информации 8-битный, русифицированный). Рассмотрим ситуацию, когда письмо, содержащее русский текст, отправлено по электронной почте. Случалось, что в процессе путешествия по сетям письмо обрабатывалось программой, которая работала с 7-битной кодировкой и обнуляла восьмой бит. В результате такого преобразования код символа уменьшался на 128, превращаясь в код символа латинского алфавита. Возникла необходимость повысить устойчивость передаваемой текстовой информации к обнулению 8 бита.

      К счастью, значительное число букв кириллицы имеет фонетические аналоги в латинском алфавите. Например, Ф и F, Р и R. Есть несколько букв, совпадающих даже по начертанию. Расположив русские буквы в кодовой таблице таким образом, чтобы их код превышал код аналогичных латинских на число 128, добились того, что потеря 8-го бита превращала текст хотя и в состоящий из одной латиницы, но все равно понимаемый русскоязычным пользователем.

      Так как из всех операционных систем, распространенных в то время, самыми удобными средствами работы с сетью обладали различные клоны операционной системы Unix, то эта кодировка стала фактическим стандартом в этих системах. Таковой она является и сейчас в ОС Linux. И именно эта кодировка чаще всего применяется для обмена почтой и новостями в Интернет.

      Далее наступила эра персональных компьютеров и операционной системы MS DOS. Как выяснилось, кодировка Koi8-R для нее не подходила (так же, как и ISO 8859-5), в ее таблице некоторые русские буквы находились на тех местах, которые многие программы предполагали заполненными псевдографикой (горизонтальные и вертикальные черточки, уголки и т. д.). Поэтому была придумана еще одна кодировка кириллицы, в таблице которой русские буквы "обтекали" со всех сторон графические символы. Назвали эту кодировку альтернативной (alt), поскольку она была альтернативой официальному стандарту -- кодировке ISO-8859-5. Неоспоримым достоинством этой кодировки является то, что русские буквы в ней расположены в алфавитном порядке.

      После появления ОС Windows от фирмы Microsoft выяснилось, что альтернативная кодировка по некоторым причинам для нее не подходит. Снова передвинув русские буквы в таблице (появилась возможность -- ведь псевдографика в Windows не требуется), получили кодировку Windows 1251 (Win-1251).

      Но компьютерные технологии постоянно совершенствуются и в настоящее время все большее число программ начинает поддерживать стандарт Unicode, который позволяет кодировать практически все языки и диалекты жителей Земли.

      Итак, в различных ОС предпочтение отдается разным кодировкам. Для того чтобы стало возможным чтение и редактирования текста, набранного в другой кодировке, используются программы перекодирования русского текста. Некоторые текстовые редакторы содержат встроенные перекодировщики, позволяющие читать текст в различных кодировках (Word и др.). Мы для перекодировки файлов будем использовать ряд утилит в ОС Linux, назначение которых ясно из названия: alt2koi, win2koi, koi2win, alt2win, win2alt, koi2alt (откуда, куда, цифра 2 (two) схожа по звучанию с предлогом to, указывающим направление). Эти команды имеют одинаковый синтаксис: команда выходной_файл.

      Перекодируем текст, набранный в редакторе Edit в среде MS DOS, в кодировку Koi8-R. Для этого выполним команду

      alt2koi file1.txt > filenew

      Так как в MS DOS и Linux по разному кодируется перевод строки, рекомендуется выполнить еще команду "fromdos":

      fromdos filenew > file2.txt

      Команда с обратным действием называется "todos" и имеет такой же синтаксис.

      Отсортируем файл List.txt, содержащий список фамилий и подготовленный в кодировке Koi8-R, в алфавитном порядке. Воспользуемся командой sort, которая сортирует текстовый файл по возрастанию или убыванию кодов символов. Если применить ее сразу, то, например, буква В окажется в конце списка, аналогично соответствующей ей букве латинского алфавита V . Вспомнив, что в альтернативной кодировке русские буквы расположены строго по алфавиту, выполним ряд операций: перекодируем текст в альтернативную кодировку, отсортируем его и снова вернем в кодировку Koi8-R. С использованием конвейера команд получаем

      koi2alt List.txt | sort | alt2koi > List_Sort.txt

      В современных дистрибутивах ОС Linux решены многие проблемы, связанные с локализацией программного обеспечения. В частности утилита sort теперь учитывает особенности кодировки Koi8-R и для сортировки файла в алфавитном порядке достаточно выполнить команду