Кодування кирилиці - російської мови

Повна підтримка діректів.htaccess додається ...

Пролонгації домену 199-00 руб

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

Стандартизована тільки половина таблиці, т.зв. 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 треба тільки з урахуванням деяких нюансів:

    По - перше, кодування символів тексту повинне відповідати кодуванню, зазначеному в тезі.

    По - друге, сервер не повинен міняти кодування тексту при обробці запиту браузера.

    По - третє, якщо сервер змінює кодування тексту, він повинен скорегувати або видалити Meta-тег 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  byte koi8Data = ...; // Перетворимо з ЯКІ-8 в Unicode  String string = new String (koi8Data, "KOI8_R"); // Перетворимо з Unicode в Windows-1251  byte winData = string.getBytes ( "Cp1251");

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

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

// Перетворимо з Unicode в UnicodeLittleUnmarked  byte data = string.getBytes ( "UnicodeLittleUnmarked");

При подібних перетвореннях легко помилитися - якщо кодування байтових даних не соотвествуют вказаною параметру при перетворенні з 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:

InputStream is = ..; int b; StringBuffer sb = new StringBuffer (); while ((b = is.read ())! = - 1) (sb.append ((char) b); // <- так делать нельзя    ) String s = sb.toString ();

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

InputStream is = ..; int b; ByteArrayOutputStream baos = new ByteArrayOutputStream (); while ((b = is.read ())! = - 1) (baos.write (b);) // Перекодування байтів на рядок з використанням кодування за замовчуванням  String s = baos.toString (); // Якщо потрібна конкретна кодування - просто вкажіть її при виклику toString (): // // s = baos.toString ( "Cp1251");
Більш докладно про поширені помилки дивіться розділ.

8-ми бітові кодування російських букв

Ось основні 8-ми бітові кодування російських букв, які поширені:

Крім основної назви можна використовувати синоніми. Набір їх може відрізнятися в різних версіях JDK. Ось список від JDK 1.3.1:

  • Cp1251:
    • Windows-1251
  • Cp866:
    • IBM866
    • IBM-866
    • CP866
    • CSIBM866
  • KOI8_R:
    • KOI8-R
    • CSKOI8R
  • ISO8859_5:
    • 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 варіанті. Іноді через це можна нарватися на граблі з запуском програми, навіть якщо їй не потрібні російські літери. Типова помилка, що виникає при цьому:

Error occurred during initialization of VM java / lang / ClassNotFoundException: sun / io / ByteToCharCp1251

Виникає вона, як не важко здогадатися, через те, що JVM, виходячи з російських регіональних налаштувань намагається встановити кодування за замовчуванням в Cp1251, але, тому що клас підтримки такий відсутній в US версії, закономірно обламується.

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

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

Для читання / записі не перетворених байтів використовуються потоки байтів. Якщо відомо, що байти представляють собою тільки символи в деякій кодуванні, можна використовувати спеціальні класи-перетворювачі InputStreamReader і OutputStreamWriter, щоб отримати потік символів і працювати безпосередньо з ним. Зазвичай це зручно в разі звичайних текстових файлів або при роботі з багатьма мережевими протоколами Internet. Кодування символів при цьому вказується в конструкторі класу-перетворювача. приклад:

// Рядок Unicode String string = "..."; // Записуємо рядок в текстовий файл в кодуванні Cp866  PrintWriter pw = new PrintWriter // клас з методами записи рядків   (New OutputStreamWriter // клас-перетворювач   (New FileOutputStream ( "file.txt"), "Cp866"); pw.println (string); // записуємо рядок в файл  pw.close ();

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

// Рядок Unicode String string = "..."; // Записуємо рядок в текстовий файл в двох кодуваннях (Cp866 і Cp1251)  OutputStream os = new FileOutputStream ( "file.txt"); // клас записи байтів в файл // Записуємо рядок в кодуванні Cp866  os.write (string.getBytes ( "Cp866")); // Записуємо рядок в кодуванні Cp1251  os.write (string.getBytes ( "Cp1251")); os.close ();

Консоль в Java традиційно представлена ​​потоками, але, на жаль, не символів, а байтів. Справа в тому, що потоки символів з'явилися тільки в JDK 1.1 (разом з усім механізмом кодувань), а доступ до консольного вводу / виводу проектувався ще в JDK 1.0, що і призвело до появи виродка у вигляді класу PrintStream. Цей клас використовується в змінних System.out і System.err, які власне і дають доступ до висновку на консоль. За всіма ознаками це потік байтів, але з купою методів запису рядків. Коли Ви записуєте в нього рядок, всередині відбувається конвертація в байти з використанням кодування за замовчуванням, що в разі виндов, як правило, є неприйнятним - кодування за замовчуванням буде Cp1251 (Ansi), а для консольного вікна зазвичай потрібно використовувати Cp866 (OEM). Ця помилка була зареєстрована ще в 97-му році () але Sun-вівці виправляти її начебто не поспішають. Так як методу установки кодування в PrintStream немає, для вирішення цієї проблеми можна підмінити стандартний клас на власний за допомогою методів System.setOut () і System.setErr (). Ось, наприклад, звичайне початок в моїх програмах:

  ... public static void main (String args) ( // Установка виведення консольних повідомлень із належним кодуванням   try (String consoleEnc = System.getProperty ( "console.encoding", "Cp866"); System.setOut (new CodepagePrintStream (System.out, consoleEnc)); System.setErr (new CodepagePrintStream (System.err, consoleEnc)); ) catch (UnsupportedEncodingException e) (System.out.println ( "Unable to setup console codepage:" + e);) ...

Вихідні тексти класу 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:

wrapper.env = NLS_LANG = American_America.CL8KOI8R
Інформацію про це надіслав Сергій Безруков, за що йому окреме спасибі.

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

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

// Параметри з'єднання з базою  Properties connInfo = new Properties (); connInfo.put ( "CODEPAGEID", "66"); // Кодування Cp866 // Встановлюємо з'єднання  Connection db = DriverManager.getConnection ( "jdbc: DBF: / C: / MyDBFFiles", connInfo);
Якщо у Вас DBF-файли формату FoxPro, то у них своя специфіка. Справа в тому, що FoxPro зберігає в заголовку файлу ID кодової сторінки (байт зі зміщенням 0x1D), яка використовувалася при створенні DBF-ки. При відкритті таблиці драйвер використовує значення з заголовка, а не параметр "CODEPAGEID" (параметр в цьому випадку використовується тільки при створенні нових таблиць). Відповідно, щоб все працювало нормально, DBF-файл повинен бути створений з правильним кодуванням - інакше будуть проблеми.

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

З цим драйвером теж все досить просто:

// Параметри з'єднання з базою  Properties connInfo = new Poperties (); connInfo.put ( "user", user); connInfo.put ( "password", pass); connInfo.put ( "useUnicode", "true"); connInfo.put ( "characterEncoding", "KOI8_R"); Connection conn = DriverManager.getConnection (dbURL, props);

InterBase (interbase.interclient.Driver)

Для цього драйвера працює параметр "charSet":
// Параметри з'єднання з базою Properties connInfo = new Properties (); connInfo.put ( "user", username); connInfo.put ( "password", password); connInfo.put ( "charSet", "Cp1251"); // Встановлюємо з'єднання  Connection db = DriverManager.getConnection (dataurl, connInfo);

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

CREATE DATABASE 'E: ProjectHoldingDataBaseHOLDING.GDB' PAGE_SIZE 4096 DEFAULT CHARACTER SET UNICODE_FSS; CREATE TABLE RUSSIAN_WORD ( "NAME1" VARCHAR (40) CHARACTER SET UNICODE_FSS NOT NULL, "NAME2" VARCHAR (40) CHARACTER SET WIN1251 NOT NULL, PRIMARY KEY ( "NAME1"));

У версії 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. приклад:

// Установка кодування для результатів запиту // Врахуйте, що деякі engine не допускають // наявність прогалини між ';' і 'charset'   response.setContentType ( "text / html; charset = UTF-8"); PrintWriter out = response.getWriter (); // Налагоджувальний висновок назви кодування для перевірки   out.println ( "Encoding:" + response.getCharacterEncoding ()); ... out.close (); )

Це з приводу віддачі відповідей клієнту. З вхідними параметрами, на жаль не так просто. Вхідні параметри кодуються оглядачем побайтно відповідно до MIME-типом "application / x-www-form-urlencoded". Як розповів Олексій Мендель російські літери браузери кодують, використовуючи поточну встановлену кодування. Ну і, зрозуміло, нічого про неї не повідомляють. Відповідно, наприклад, в JSDK версій від 2.0 до 2.2 це ніяк не перевіряється, а то, що за кодування буде використана для перетворення - залежить від використовуваного engine. Починаючи з специфікації 2.3 з'явилася можливість встановлювати кодування для javax.servlet.ServletRequest - метод setCharacterEncoding (). Цю специфікацію вже підтримують останні версії Resin і Tomcat.

Таким чином, якщо Вам пощастило, і у Вас стоїть сервер з підтримкою Servlet 2.3, то все досить просто:

public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException ( // Кодування повідомлень   request.setCharacterEncoding ( "Cp1251"); String value = request.getParameter ( "value"); ...

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

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

Приклад такого фільтра:

import java.io. *; import java.util. *; import javax.servlet. *; import javax.servlet.http. *; public class CharsetFilter implements Filter (// кодування private String encoding; public void init (FilterConfig config) throws ServletException ( // читаємо з конфігурації   encoding = config.getInitParameter ( "requestEncoding"); // якщо не встановлена ​​- встановлюємо Cp1251   if (encoding == null) encoding = "Cp1251"; ) Public void doFilter (ServletRequest request, ServletResponse response, FilterChain next) throws IOException, ServletException (request.setCharacterEncoding (encoding); next.doFilter (request, response);) public void destroy () ())

І його конфігурації в web.xml:

Charset Filter CharsetFilter Charset Filter /*

Якщо ж Вам не пощастило, і у Вас більше стара версія  - для досягнення результату доведеться поізвращаться:

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

  • FOP

    Пакет 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, все в один рядок):
      java -cp.; c: fop-0.20.1uildfop.jar; c: fop-0.20.1libatik.jar; c: fop-0.20.1libxalan-2.0.0.jar; c: fop-0.20.1libxerces.jar; c: fop-0.20.1libjimi-1.0.jar org.apache.fop.fonts.apps.TTFReader fontsarial.ttf fontsarial.xml
    3. У FOP додати в conf / userconfig.xml опис шрифту з російськими буквами, типу:
      Аналогічно додаються Arial normal / bold, italic / normal і italic / bold.
    4. При виклику FOP з командного рядка  після org.apache.fop.apps.Fop писати -c c: fop-0.20.1confuserconfig.xml Якщо потрібно використовувати FOP з сервлета, то потрібно в сервлет після рядка
        Driver driver = new Driver ();
      додати рядки:
      // Каталог fonts (c: weblogicfonts) був створений виключно для зручності.  String userConfig = "fonts / userconfig.xml"; File userConfigFile = new File (userConfig); Options options = new Options (userConfigFile);
      Тоді розташування файлів ttf у файлі userconfig.xml можна вказати щодо кореня сервера додатки, без вказівки абсолютного шляху:
    5. У файлі FO (або XML і XSL) перед використанням шрифту писати:
      font-family = "Arial" font-weight = "bold" (Якщо використовується Arial bold) font-style = "italic" (Якщо використовується Arial italic)

    Даний алгоритм надіслав Олексій Тюрін, за що йому окреме спасибі.

    Якщо Ви використовуєте вбудований в 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

    У стандарті CORBA передбачений тип, відповідний Java-івської типу String. Це тип wstring. Все б добре, але деякі 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  byte koi8Data = ...; // Перетворимо з ЯКІ-8 в Unicode  String string = new String (koi8Data, "KOI8_R"); // Перетворимо з Unicode в Windows-1251  byte winData = string.getBytes ( "Cp1251");

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

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

// Перетворимо з Unicode в UnicodeLittleUnmarked  byte data = string.getBytes ( "UnicodeLittleUnmarked");

При подібних перетвореннях легко помилитися - якщо кодування байтових даних не соотвествуют вказаною параметру при перетворенні з 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:

InputStream is = ..; int b; StringBuffer sb = new StringBuffer (); while ((b = is.read ())! = - 1) (sb.append ((char) b); // <- так делать нельзя    ) String s = sb.toString ();

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

InputStream is = ..; int b; ByteArrayOutputStream baos = new ByteArrayOutputStream (); while ((b = is.read ())! = - 1) (baos.write (b);) // Перекодування байтів на рядок з використанням кодування за замовчуванням  String s = baos.toString (); // Якщо потрібна конкретна кодування - просто вкажіть її при виклику toString (): // // s = baos.toString ( "Cp1251");
Більш докладно про поширені помилки дивіться розділ.

8-ми бітові кодування російських букв

Ось основні 8-ми бітові кодування російських букв, які поширені:

Крім основної назви можна використовувати синоніми. Набір їх може відрізнятися в різних версіях JDK. Ось список від JDK 1.3.1:

  • Cp1251:
    • Windows-1251
  • Cp866:
    • IBM866
    • IBM-866
    • CP866
    • CSIBM866
  • KOI8_R:
    • KOI8-R
    • CSKOI8R
  • ISO8859_5:
    • 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 варіанті. Іноді через це можна нарватися на граблі з запуском програми, навіть якщо їй не потрібні російські літери. Типова помилка, що виникає при цьому:

Error occurred during initialization of VM java / lang / ClassNotFoundException: sun / io / ByteToCharCp1251

Виникає вона, як не важко здогадатися, через те, що JVM, виходячи з російських регіональних налаштувань намагається встановити кодування за замовчуванням в Cp1251, але, тому що клас підтримки такий відсутній в US версії, закономірно обламується.

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

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

Для читання / записі не перетворених байтів використовуються потоки байтів. Якщо відомо, що байти представляють собою тільки символи в деякій кодуванні, можна використовувати спеціальні класи-перетворювачі InputStreamReader і OutputStreamWriter, щоб отримати потік символів і працювати безпосередньо з ним. Зазвичай це зручно в разі звичайних текстових файлів або при роботі з багатьма мережевими протоколами Internet. Кодування символів при цьому вказується в конструкторі класу-перетворювача. приклад:

// Рядок Unicode String string = "..."; // Записуємо рядок в текстовий файл в кодуванні Cp866  PrintWriter pw = new PrintWriter // клас з методами записи рядків   (New OutputStreamWriter // клас-перетворювач   (New FileOutputStream ( "file.txt"), "Cp866"); pw.println (string); // записуємо рядок в файл  pw.close ();

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

// Рядок Unicode String string = "..."; // Записуємо рядок в текстовий файл в двох кодуваннях (Cp866 і Cp1251)  OutputStream os = new FileOutputStream ( "file.txt"); // клас записи байтів в файл // Записуємо рядок в кодуванні Cp866  os.write (string.getBytes ( "Cp866")); // Записуємо рядок в кодуванні Cp1251  os.write (string.getBytes ( "Cp1251")); os.close ();

Консоль в Java традиційно представлена ​​потоками, але, на жаль, не символів, а байтів. Справа в тому, що потоки символів з'явилися тільки в JDK 1.1 (разом з усім механізмом кодувань), а доступ до консольного вводу / виводу проектувався ще в JDK 1.0, що і призвело до появи виродка у вигляді класу PrintStream. Цей клас використовується в змінних System.out і System.err, які власне і дають доступ до висновку на консоль. За всіма ознаками це потік байтів, але з купою методів запису рядків. Коли Ви записуєте в нього рядок, всередині відбувається конвертація в байти з використанням кодування за замовчуванням, що в разі виндов, як правило, є неприйнятним - кодування за замовчуванням буде Cp1251 (Ansi), а для консольного вікна зазвичай потрібно використовувати Cp866 (OEM). Ця помилка була зареєстрована ще в 97-му році () але Sun-вівці виправляти її начебто не поспішають. Так як методу установки кодування в PrintStream немає, для вирішення цієї проблеми можна підмінити стандартний клас на власний за допомогою методів System.setOut () і System.setErr (). Ось, наприклад, звичайне початок в моїх програмах:

  ... public static void main (String args) ( // Установка виведення консольних повідомлень із належним кодуванням   try (String consoleEnc = System.getProperty ( "console.encoding", "Cp866"); System.setOut (new CodepagePrintStream (System.out, consoleEnc)); System.setErr (new CodepagePrintStream (System.err, consoleEnc)); ) catch (UnsupportedEncodingException e) (System.out.println ( "Unable to setup console codepage:" + e);) ...

Вихідні тексти класу 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:

wrapper.env = NLS_LANG = American_America.CL8KOI8R
Інформацію про це надіслав Сергій Безруков, за що йому окреме спасибі.

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

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

// Параметри з'єднання з базою  Properties connInfo = new Properties (); connInfo.put ( "CODEPAGEID", "66"); // Кодування Cp866 // Встановлюємо з'єднання  Connection db = DriverManager.getConnection ( "jdbc: DBF: / C: / MyDBFFiles", connInfo);
Якщо у Вас DBF-файли формату FoxPro, то у них своя специфіка. Справа в тому, що FoxPro зберігає в заголовку файлу ID кодової сторінки (байт зі зміщенням 0x1D), яка використовувалася при створенні DBF-ки. При відкритті таблиці драйвер використовує значення з заголовка, а не параметр "CODEPAGEID" (параметр в цьому випадку використовується тільки при створенні нових таблиць). Відповідно, щоб все працювало нормально, DBF-файл повинен бути створений з правильним кодуванням - інакше будуть проблеми.

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

З цим драйвером теж все досить просто:

// Параметри з'єднання з базою  Properties connInfo = new Poperties (); connInfo.put ( "user", user); connInfo.put ( "password", pass); connInfo.put ( "useUnicode", "true"); connInfo.put ( "characterEncoding", "KOI8_R"); Connection conn = DriverManager.getConnection (dbURL, props);

InterBase (interbase.interclient.Driver)

Для цього драйвера працює параметр "charSet":
// Параметри з'єднання з базою Properties connInfo = new Properties (); connInfo.put ( "user", username); connInfo.put ( "password", password); connInfo.put ( "charSet", "Cp1251"); // Встановлюємо з'єднання  Connection db = DriverManager.getConnection (dataurl, connInfo);

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

CREATE DATABASE "E: \\ ProjectHolding \\ DataBase \\ HOLDING.GDB" PAGE_SIZE 4096 DEFAULT CHARACTER SET UNICODE_FSS; CREATE TABLE RUSSIAN_WORD ( "NAME1" VARCHAR (40) CHARACTER SET UNICODE_FSS NOT NULL, "NAME2" VARCHAR (40) CHARACTER SET WIN1251 NOT NULL, PRIMARY KEY ( "NAME1"));

У версії 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. приклад:

// Установка кодування для результатів запиту // Врахуйте, що деякі engine не допускають // наявність прогалини між ";" і "charset"   response.setContentType ( "text / html; charset = UTF-8"); PrintWriter out = response.getWriter (); // Налагоджувальний висновок назви кодування для перевірки   out.println ( "Encoding:" + response.getCharacterEncoding ()); ... out.close (); )

Це з приводу віддачі відповідей клієнту. З вхідними параметрами, на жаль не так просто. Вхідні параметри кодуються оглядачем побайтно відповідно до MIME-типом "application / x-www-form-urlencoded". Як розповів Олексій Мендель російські літери браузери кодують, використовуючи поточну встановлену кодування. Ну і, зрозуміло, нічого про неї не повідомляють. Відповідно, наприклад, в JSDK версій від 2.0 до 2.2 це ніяк не перевіряється, а то, що за кодування буде використана для перетворення - залежить від використовуваного engine. Починаючи з специфікації 2.3 з'явилася можливість встановлювати кодування для javax.servlet.ServletRequest - метод setCharacterEncoding (). Цю специфікацію вже підтримують останні версії Resin і Tomcat.

Таким чином, якщо Вам пощастило, і у Вас стоїть сервер з підтримкою Servlet 2.3, то все досить просто:

public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException ( // Кодування повідомлень   request.setCharacterEncoding ( "Cp1251"); String value = request.getParameter ( "value"); ...

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

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

Приклад такого фільтра:

import java.io. *; import java.util. *; import javax.servlet. *; import javax.servlet.http. *; public class CharsetFilter implements Filter (// кодування private String encoding; public void init (FilterConfig config) throws ServletException ( // читаємо з конфігурації   encoding = config.getInitParameter ( "requestEncoding"); // якщо не встановлена ​​- встановлюємо Cp1251   if (encoding == null) encoding = "Cp1251"; ) Public void doFilter (ServletRequest request, ServletResponse response, FilterChain next) throws IOException, ServletException (request.setCharacterEncoding (encoding); next.doFilter (request, response);) public void destroy () ())

І його конфігурації в web.xml:

Charset Filter CharsetFilter Charset Filter /*

Якщо ж Вам не пощастило, і у Вас старіша версія - для досягнення результату доведеться поізвращаться:

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

  • FOP

    Якщо програма не працює ніде - значить проблема тільки в ній і в Ваших руках. Уважно перечитайте все, що було написано вище, і шукайте. Якщо ж проблема проявляється тільки в конкретному оточенні - значить справа, можливо в налаштуваннях. Де саме - залежить від того, який графічною бібліотекою Ви користуєтеся. Якщо 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:

    public class Test (public static void main (String args) (System.out.println ( "ЙЦУКЕНГШЩЗХ'");))
    в Far-е зберігаємо даний код  в файл Test.java, компіляем ...
      C: \\\u003e javac Test.java
    і запускаємо ...
    C: \\\u003e java Test ЙЦУКЕНГ? ЩЗХ'

    Що ж сталося? Куди поділася буква Ш? Весь фокус тут в тому, що сталася взаємокомпенсації двох помилок. Текстовий редактор  в 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), що може дещо розходиться з очікуваною. В результаті, отримавши невірні коду букв (через невірну кодування) можна витратити дуже багато часу на пошук помилок в абсолютно правильному коді.

    Про метод перекодування символів

    Цей метод багато хто використовує неправильно, напевно, не зовсім розуміючи його суть і обмеження. Він призначений для відновлення вірних кодів букв, якщо вони були невірно проінтерпретовані. Суть методу проста: з отриманих невірних символів, використовуючи відповідну кодову сторінку, відновлюється вихідний масив байтів. Потім з цього масиву байтів, використовуючи вже коректну сторінку, виходять нормальні коду символів. приклад:

    String res = new String (src.getBytes ( "ISO-8859-1"), "Cp1251");

    Проблем у використанні цього прийому може бути кілька. Наприклад, для відновлення використовується невірна сторінка, або ж вона може змінитися в деяких ситуаціях. Інша проблема може бути в тому, що деякі сторінки виконують неоднозначне перетворення 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 (-\u003e xkb) в AWT. (Це не є погано саме по собі, просто з такими штуками потрібно звертатися обережно + деякі приладнати xkb не люблять).

    Налаштувати xkb (і локаль (xkb без локалі НЕ ПРАЦЮЄ))

    процедура:

    1. виставляється локаль (де-небудь типу в / etc / profile або в ~ / .bash_profile)
        export LANG = ru_RU.KOI8-R export LC_ALL = ru_RU.KOI8-R
    2. правиться (якщо це ще не зроблено) / etc / X11 / XF86Config. У секції Keyboard має бути приблизно наступне:
        XkbKeycodes "xfree86" XkbTypes "default" XkbCompat "default" XkbSymbols "ru" XkbGeometry "pc" XkbRules "xfree86" XkbModel "pc101" XkbLayout "ru" XkbOptions "grp: shift_toggle" # перемикання 2-мя шифт #XkbOptions "grp: caps_toggle "# перемикання caps-lock" ом
      примітка: така настройка xkb не сумісна з xrus (і йому подібними типу kikbd) а тому з ними доведеться розпрощатися.
    3. перезапускати X-и. Потрібно перевірити щоб все працювало (типу російські літери в терміналі і додатках)
    4. font.properties.ru -\u003e $ JAVA_HOME / jre / lib
    5. fonts.dir -\u003e $ JAVA_HOME / jre / lib / fonts
    6. cd $ JAVA_HOME / jre / lib / fonts; rm fonts.scale; ln -s fonts.dir fonts.scale

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

Якщо Ви помітите неточність в описі або захочете доповнити, то напишіть мені про це, і Ваше ім'я так само з'явиться в цьому списку. :-)