23 мар. 2015 г.

Лексическая структура Java. Часть 1 – Unicode и другие кодировки

В предыдущей части были представлены видео на которых было показано создание классической программы HelloWorld на Java в различных средах разработки. Но там есть один ньюансик. Обычно среды разработки могут определить кодировку текста в которой была написана программа и правильно ее откомпилировать, чтобы текст, отличный от латинского алфавита правильно отображался в консоли и вообще в программах.

GrabliСразу скажу, что программы на Java лучше создавать в кодировке Unicode (UTF-8), так как если вы будете писать их в других кодировках, то могут быть проблемы с отображением символов национальных алфавитов, отличных от латинского. Хотя они могут быть даже если вы их пишете в Unicode и сейчас мы в этом убедимся.

Теперь в бой! Посмотрим все на примерах. Возьмем классическую программу Hello World и допишем в нее строчку выводящую сообщение “Привет Мир!” на русском языке. Текст программы запишем как и полагается в кодировке UTF-8.

E00001

И теперь откомпилируем ее с параметрами по умолчанию. То есть просто дадим команду

javac HelloWorld.java

И выполним нашу программу командой

java HelloWorld

И смотрим результат

E00002

Текст на русском языке “Привет Мир!” отобразился кракозябрами. Что же случилось? Почему такая не справедливость? Ведь мы же записали текст программы в Unicode! Сделали все как положено!

Но дело в том, что компилятор javac, по умолчанию, компилирует программу в кодовой странице операционной системы.

Теперь откомпилируем эту же программу, но уже укажем в какой кодировке у нас исходный код программы c помощью следующей команды

javac -encoding utf8 HelloWorld.java

И запустим программу на выполнение

E00003

Ну вот теперь русский текст “Привет Мир!” отображается правильно.

Как правило, среды разработки многие параметры компилятора, заботливо, подставляют за разработчика, но все же хорошо бы разбираться в том, что происходит.

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

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

Сейчас рассмотрим еще несколько примеров исходников в различных кодировках для лучшего понимания темы и на этом закончим с использованием текстового редактора, хотя часто он бывает нужен, чтобы просто заглянуть в текст программы не запуская “тяжелую” IDE.

И так я создал такую же простую программу как “Hello World”, но в разных кодировках (866, 1251 и UTF-8).

Код абсолютно одинаковый только создан в разных кодировках.

E00004

E00005

E00007

Теперь скомпилируем их с параметрами по умолчанию и запустим.

Но сперва отметим такую команду консоли Windows как chcp. Про нее мало кто знает. Она отображает текущую кодовую страницу консоли, а так же может ее устанавливать.

Посмотрим текущую кодовую страницу консоли, откомпилируем программу Example866.java и запустим ее.

E00008

Как видим вывод русского текста не правильный, одни кракозабры, зато слово Java и восклицательный знак (!) вывелись правильно. Это происходит потому что Unicod код латинских символов (первых 128) совпадает с ASCII и Latin-1. Поэтому если в вашей программе вы используете только латинский алфавит для отображения строк и т.п., то можно не беспокоится. Все всегда будет отображаться корректно, но с символами других алфавитов это не так.

В данном случае, как видим, не смотря на то, что даже кодовая страница консоли у нас 866 и файл с исходником тоже в этой же кодировке, вывод все равно отображается не правильно.

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

E00009

Опять все отобразилось не правильно, за исключением слова Java и восклицательного знака. Но кракозябры уже другие, что безусловно не может не радовать…

И так в чем же проблема? А проблема в том, что как уже я говорил, по умолчанию компилятор использует кодовую страницу операционной системы, в нашем случае это CP1251, так как Windows у нас настроен на использование кириллической кодировки CP1251.

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

E00010

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

Чтобы исправить эту ситуацию надо откомпилировать программу Exemple866.java с использованием кодовой страницы 866. И чтобы проверить это вернем консоли кодировку по умолчанию 866, дабы все было по честно по правилам.

E00011

Теперь у нас все правильно выводится.

Далее откомпилируем программу Example1251.java и запустим ее. Ее можно компилировать с параметрами по умолчанию, так как javac будет использовать для нее кодировку 1251, что нам и нужно.

E00013

Все тоже отображается правильно. Ну и тоже самое с программой ExampleUTF8.java

E00014

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

Теперь небольшой ликбез об UTF8. Данная кодировка использует ДВА БАЙТА для хранения кода символа, что позволяет представить 65535 символов – это покрывает почти все символы всех языков Земли. Кодировка ASCII может представить только 256 символов.

Теперь посмотрим нашу самую русскую букву Ё в кодировке 1251 (ASCII) и в кодировке UTF8.

В кодировке 1251 ASCII буква Ё представлена одним байтом (A8).

E00015

В кодировке UTF-8 буква Ё представлена двумя байтами (D0 81).

E00016

И теперь посмотрим бинарные файлы классов Example866.class и Example1251.class

E00017

E00018

Как видим наш текст на русском “Это Java программа!” в обоих бинарниках отображается одинаково, хотя один из них сделан из исходника в кодировке 866, а другой в кодировке 1251. Это произошло потому, что компилятор javac, перевел символы из этих кодировок в кодировку UTF-8.

Так же я подчеркнул байты CA FE BA BE – это так называемое магическое число java. По нему виртуальная машина определяет что перед ней именно класс Java, а не что-то еще. Эта комбинация присутствует в начале всех откомпилированных  файлов классов Java с расширением .class.

Ну и чтобы все было более наглядней приведу еще одни скрин сравнения этих файлов

 E00019

sber01Так вот! К чему я это все?

Граждане, храните деньги в сберегательной кассе!

Пишите исходники в UTF-8. Надежно, выгодно, удобно.

И да! Храните ваши исходники на bitbucket.org – надежно, выгодно, удобно!

P.S. Все исходники примеров тут: https://bitbucket.org/n0tb0dy/studyingjava

2 комментария:

  1. У меня вопрос. Я сохраняю файл Test.java в utf8, потом компилирую его с опцией -encoding utf8 и получаю ошибку:
    ... illegal character: \ufuff
    ?import java.awt.*;
    ^
    и указание на самый первый символ в файле.

    ОтветитьУдалить
    Ответы
    1. А зачем вы его компилируете с этой опцией?

      Удалить