17. Типичные ошибки, или "куда делась буква Ш?"
Буква Ш
Этот вопрос ("куда делась буква Ш?") довольно часто возникает у начинающих программистов на Java.
Давайте разберёмся, куда же она действительно чаще всего девается. :-)
Вот типичная программа а-ля HelloWorld:
public class Test {
public static void main(String[] args) {
System.out.println("ЙЦУКЕНГШЩЗХЪ");
}
}
в Far-е сохраняем данный код в файл Test.java, компиляем...
C:\>javac Test.java
и запускаем...
C:\>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.
- Если Вы увидите "??????" (или любые другие символы кроме "АБВ" на месте второй тройки букв), значит вывод у Вас работает неверно.
Настроив вывод и компиляцию уже можно легко разобраться и со входом. После настройки всей цепочки проблемы должны исчезнуть.