Последнее обновление:
Почему заглавная буква И иногда кодируется с ошибкой?
Что такое кодировка и для чего она используется в вебпрограммировании, объяснять не будем. Надеемся, практически каждый разработчик сайтов сталкивался с необходимостью применения кодирования.
Если символы текста (например, html_кода) принадлежат латинскому алфавиту или являются символами в прямом смысле этого слова, например, представляют собой точку, запятую, восклицательный или вопросительный знаки и т.п., то проблем с ними, как правило, не возникает. А вот с алфавитом другого языка, например, русского, могут возникнуть непредвиденные проблемы.
Поговорим об одной из ошибок, которая может встретиться при кодировании из кодировки Windows-1251 в кодировку UTF-8 или обратно.
Речь идет об ошибке, которую иной раз очень трудно «выловить», так как она, фактически, ЕДИНСТВЕННАЯ. И касается она только заглавной буквы И.
Как известно, язык РНР работает с данными в кодировке UTF-8. Тогда как на вебстранице может быть, вообще говоря, любая кодировка, например, Windows-1251. Соответственно, если данные переданы с этой вебстраницы (например, путем технологии AJAX), то их следует перекодировать в UTF-8, например, при помощи команды:
$data = mb_convert_encoding($data, "utf-8", "cp1251");
Если же необходимо записать данные в файл, который будет открываться в кодировке Windows-1251, то целесообразно осуществить обратную процедуру перекодировки. Кроме того, чтобы вывести русский текст при помощи РНР, опять-таки, необходимо также его вначале перекодировать в Windows-1251, иначе возникнут нечитаемые символы типа если СѓРєР°
Многие находят такие процедуры утомительными (от некоторых программистов, мнящих себя специалистами, я даже слышал, что это, мол, очередная «головная боль»), поэтому принимают решение ВЕЗДЕ использовать универсальную кодировку, коей и является UTF-8.
Конечно, подобный подход может являться целесообразным. Но, если по каким-то причинам невозможно или попросту не хочется его применять? Например, если изначально сайт создавался в другой кодировке, т.е. не в UTF-8? В самом деле, не переделывать же весь сайт только по причине сложности работы с кодировками.
Проблемы с заглавной буквой И
Проблема может возникнуть в ситуации, когда по ошибке или недосмотру кодирование в UTF-8 (или, наоборот, из нее) было сделано ПОДРЯД более одного раза.
Самое интересное, что все другие буквы русского алфавита, как строчные, так и прописные (заглавные) без проблем проходят процедуру двойного кодирования и UTF-8 и, соответственно, двойного декодирования из нее. Но, только не заглавная буква И.
Чтобы убедиться в этом, мы создали небольшой РНР-код, который демонстрирует приведенные выше рассуждения. Вот, кстати, этот код - см. ниже.
Исходный код программы
<?php
$str1 = "0123456789АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя";
$str2 ="`~!@#$%^&*()_+-=;%:[]{}.,<>\|;:
?";
$str3 = "!№;%:?*()_-+=.,\/";
$str4 = "qwertyuiop[]asdfghjklzxcvbnmQWERTYUIOP{}ASDFGHJKL:ZXCVBNM";
$str5 = "/*-789+4561230.
";
echo mb_convert_encoding("КИРИЛЛИЦА<br/>", "Windows-1251", "UTF-8");
coder($str1);
echo mb_convert_encoding("<hr/><br/>СИМВОЛЫ ЛАТИНСКИЕ<br/>", "Windows-1251", "UTF-8");
coder($str2);
echo mb_convert_encoding("<hr/><br/>СИМВОЛЫ, НАПЕЧАТАННЫЕ ПРИ ВКЛЮЧЕННОЙ (НА КЛАВИАТУРЕ) КИРИЛЛИЦЕ<br/>", "Windows-1251", "UTF-8");
coder($str3);
echo mb_convert_encoding("<hr/><br/>ЛАТИНСКИЕ БУКВЫ<br/>", "Windows-1251", "UTF-8");
coder($str4);
echo mb_convert_encoding("<hr/><br/>СИМВОЛЫ С ПРАВОЙ КЛАВИАТУРЫ<br/>", "Windows-1251", "UTF-8");
coder($str5);
function coder($str_i) {
echo mb_convert_encoding("Кодирования не было сделано:<br/>", "Windows-1251", "UTF-8");
print_letters($str_i);
$str_i = mb_convert_encoding($str_i, "Windows-1251", "UTF-8");
echo "<br/><br/>".mb_convert_encoding("Перекодировано в Windows-1251:<br/>", "Windows-1251", "UTF-8");
print_letters($str_i);
$str_i = mb_convert_encoding($str_i, "UTF-8", "Windows-1251");
echo "<br/><br/>".mb_convert_encoding("Перекодировано обратно, т.е. из Windows-1251 в UTF-8:<br/>", "Windows-1251", "UTF-8");
print_letters($str_i);
$str_i = mb_convert_encoding($str_i, "UTF-8", "Windows-1251");
echo "<br/><br/>".mb_convert_encoding("ЕЩЕ РАЗ перекодировано в UTF-8:<br/>", "Windows-1251", "UTF-8");
print_letters($str_i);
$str_i = mb_convert_encoding($str_i, "Windows-1251", "UTF-8");
echo "<br/><br/>".mb_convert_encoding("Перекодируем обратно в Windows-1251 один раз:<br/>", "Windows-1251", "UTF-8");
print_letters($str_i);
$str_i = mb_convert_encoding($str_i, "Windows-1251", "UTF-8");
echo "<br/><br/>".mb_convert_encoding("Перекодируем обратно в Windows-1251 еще раз:<br/>", "Windows-1251", "UTF-8");
print_letters($str_i);
}
function print_letters($str){
$str_length = strlen($str);
for($i=1; $i<=$str_length; $i++) {
$subString = substr($str, $i, 1);
echo $subString.'<span style="background-color:blue"> </span>';
}
}
?>
Запустить код и посмотреть результат можно на этой странице.
На что стоит обратить внимание?
Мы использовали все символы (как русские, так и латинские), которые можно ввести с типичной клавиатуры в операционной системе Windows. Конечно, символы, которых на клавиатуре нет и которые вводятся путем нажатия комбинации (одной или нескольких) функциональных клавиш мы здесь не рассматриваем.
Для наглядности, каждый символ отделен от соседних – пробелами, окрашенными в синий цвет.
Итак, что видим? Без кодирования русскоязычные символы отображаются в виде «абракадабры», что и следовало ожидать, ибо кодировка исходного документа, содержащего код РНР (ANSI как UTF-8 без BOM) и кодировка браузера, которая установлена на странице и автоматически определена браузером (т.е. кодировка Windows-1251) не совпадают.
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251"/>
Этот метатег присутствует в исходном коде просматриваемой Вами страницы.После кодирования русских букв в Windows-1251 мы видим, что они, наконец, становятся читаемыми. Перекодировка обратно в UTF-8 дает ту же «абракадабру».
Теперь попробуем ЕЩЕ РАЗ перекодировать в UTF-8. Понятно, что опять «абракадарба», только другая. Вроде бы, ничего такого плохого не сделали? Подумаешь, мол, потом исправим ошибку, дважды перекодируем обратно – и делов-то, мол… Но, не так все просто.
Итак, ДВАЖДЫ перекодируем то, что получилось, ОБРАТНО в Windows-1251. И видим, что почти все хорошо, буквы вновь читаемые, за исключением заглавной буквы И. Которая почему-то стала теперь отображаться в виде знака вопроса (?).
Denwer
получался ОДИН знак вопроса, а на нашем хостинге - ДВА.Повторимся, что со всеми остальными буквами и символами типичной клавиатуры, которые можно с нее ввести, ситуация иная: все они выдержали двойное перекодирования и являются читаемыми.
Ошибка двойной перекодировки заглавной буквы И – не повод отказываться от кодировки Windows-1251
Таким образом, можно видеть, что если применять кодировку только один раз, то все будет хорошо, никаких ошибок не возникает. А вот если более одного раза, то ошибки точно будут. По крайней мере, это касается заглавной буквы И. Если же перекодировать на дважды, а трижды, четырежды и т.д., то, вполне возможно, что подобные ошибки будут возникать и с другими русскими (кириллическими) буквами.
Кодирование и шифрование
Это стоит иметь в виду тем, кто решит использовать множественное перекодирование, например, в качестве простого средства шифрования. С одной стороны, вроде бы – сравнительно простой способ зашифровки необходимой русскоязычной информации. Ведь распознать кодировку правильно можно далеко не всегда – хоть вручную, хоть автоматически. С другой стороны, при этом будут возникать достаточно неочевидные ошибки. Так что, все же, лучше уж md5 или что-то подобное.
И, последнее. При написании этой статьи была сделана попытка оформить совокупности символов, подлежащих (пере)кодировке, в формате кода, с использованием тегов <code>. Идея, может быть, и хорошая, но успехом она не увенчалась: некоторые символы из «абракадабры» стали выглядеть, как пробелы. Поэтому пришлось оставить все, как есть, т.е. стандартное форматирование браузера.
Итог такой
Применение кодировки Windows-1251 особой помехой при разработке РНР-кода не является. Просто следует быть внимательным и не допускать ситуации многократного кодирования в одну и ту же кодировку без предварительного «возврата» в предыдущую.
Самое интересное, что ни в интернете, ни в серьезных книгах об этом информации нет. По крайней мере, автору статьи найти ее не удалось.