Преминаване на сайт към UTF-8 енкодинг
Това е публикация за програмисти, описваща решаването на реален проблем.
Наскоро ми се наложи да прехвърля стар сайт форум, написан на PHP и mySQL база данни към нов хостинг. Старият сайт беше унищожен от ненадежден хостинг доставчик, който освен базите данни беше затрил и архивните копия на множество сайтове. За щастие имах на моя компютър файловете на сайта и старо копие на базата данни. Всички съвременни хостинги по подрабиране ползват кодиране UTF-8, което осигурява безпроблемно показване на кирилицата. Моят архив с таблици на базата данни беше един текстов .sql файл, който поради спецификата на стария хостинг беше в странен енкодинг, който вместо символи на кирилица съдържаше символи като: Ãëàñóâàé
Ползвам два от най-големите хостинг доставчика в България и първо се обърнах към поддръжката на единия, след това към другия, но и двата казаха, че не могат да помогнат, защото още при експорта на данните е допусната грешка. Аз обаче продължих да опитвам и да чета кодирани текстове, които помня по памет. Например форум подписи на потребители и скоро видях, че за всеки български символ отговаря точно един от тези странните с вълнички и точки отгоре. Например:
ãóãúë îïòèìèçèðàíå - ëåñíî îòêðèâàíå íà âàøèÿ ñàéò гугъл оптимизиране - лесно откриване на вашия сайт æèâîòúò, âñåëåíàòà è âñè÷êî îñòàíàëî животът, вселената и всичко останало
Това беше добър знак, защото означава, че при това кодиране няма загуба да данни. Скоро открих, че кодирането е ASCII extended character sets - ISO_8859-1 (или ISO Latin 1).
Промяна на енкодинга на файл
Под Linux има команда iconv с която лесно се променя енкодинга на файл. Например:
iconv -f cp1251 -t utf8 db.sql > db-new.sql
От файла db.sql в CP1251 енкодинг създава нов файл db-new.sql в UTF-8 енкодинг.
Това е чудесно, но при мен не сработи и получавах грешки като:
iconv: illegal input sequence at position 114
Тази грешка показва, че iconv стига да символ, който не може да разбере и спира там. Има опция -c с която може да се укаже на програмата да игнорира непознатите символи и да продължава до края на файла.
Това при мен не сработи и влоши нещата - изчезнаха големи части от файла.
Проверката на енкодинга на файла под Linux става с команда:
file -i db.sql
Започнах да гледам подробно данните. Текстовия .sql файл, който съдържа всичките таблици и данни беше около 270MB, което е предизвикателство за много текстови редактори. Под Linux е препоръчително да се отварят подобни файлове от конзолата с команда less. Например:
less db.sql
Тя се справя с лекота с огромни текстови файлове дори при бързо превъртане или търсене напред. Има забавяне само при бързо връщане назад в текста.
С програмата less разбрах какъв е проблема - имаше смесено кодиране. Някои от таблиците на базата данни са били в cp1251 кодиране, други в latin1. Ето защо не сработваше iconv, както и други подобни програми, с които пробвах.
И тогава ми дойде хитрата идея (и вероятно единствено възможна) да направя съпоставяща таблица на символите. Трябваше за всяка буква на килилица, малка и голяма да намеря съответната буква от .sql файла. Тогава ще заменя всеки странен символ със съответния на кирилица, като по този начин ще сменя само буквите с кодиране Latin1 без да засегна буквите в cp1251. След което остава да премина към UTF8.
Използвах таблицата тук http://cstein.kings.cam.ac.uk/~chris/shtools/xterm/charsets.utf8.txt
В първата табличка последните 4 реда са главните и малките букви на кирилица, които присъстват в моя .sql файл.
Направих съпоставяща таблица и създадох нов файл със заместени символи. И разбира се, не стана от първия път. Потърсих къде греша и тази таблица http://unicode-table.com/en/sections/cyrillic/ ме подсети, че има няколко допълнителни букви на кирилица от руската азбука, които трябва да прескоча в съпоставящата таблица. Накрая стигнах до:
sed 's/À/А/g s/Á/Б/g s/Â/В/g s/Ã/Г/g s/Ä/Д/g s/Å/Е/g s/Æ/Ж/g s/Ç/З/g s/È/И/g s/É/Й/g s/Ê/К/g s/Ë/Л/g s/Ì/М/g s/Í/Н/g s/Î/О/g s/Ï/П/g s/Ð/Р/g s/Ñ/С/g s/Ò/Т/g s/Ó/У/g s/Ô/Ф/g s/Õ/Х/g s/Ö/Ц/g s/×/Ч/g s/Ø/Ш/g s/Ù/Щ/g s/Ú/Ъ/g s/Ü/Ь/g s/Þ/Ю/g s/ß/Я/g s/à/а/g s/á/б/g s/â/в/g s/ã/г/g s/ä/д/g s/å/е/g s/æ/ж/g s/ç/з/g s/è/и/g s/é/й/g s/ê/к/g s/ë/л/g s/ì/м/g s/í/н/g s/î/о/g s/ï/п/g s/ð/р/g s/ñ/с/g s/ò/т/g s/ó/у/g s/ô/ф/g s/õ/х/g s/ö/ц/g s/÷/ч/g s/ø/ш/g s/ù/щ/g s/ú/ъ/g s/ý/ь/g s/þ/ю/g s/ÿ/я/g' db.sql > db-new.sql
Тази чудна команда на Linux от команден ред, замества всеки от символите след s/ със съответния символ пред /g във файла db.sql при това с много добра бързина. Резултатът се записва във файла db-new.sql.
Отворих файла db-new.sql и всичко изглеждаше наред - базата беше спасена! Побързах да се похваля във Facebook групата Програмисти 🙂 Не знаех, че ме чака още доста работа 😉
Създаване на UTF8 база данни
За да бъде всичко наред с кирилицата, включително и сортиране по азбучен ред е нужно всичко в mySQL базата да бъде UTF8. Започва се от създаване на нова база с колация utf8_bin и всяка таблица трябва да бъде в колация utf8_bin. Важно е да се отбележи, че ако базата при създаването не е била в тази колация и я промениш след като вече има в нея таблица, обикновено това няма да помогне. Дефинициите на таблиците трябва да завършват на ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
За съжаление при мен в .sql файла имаше таблици с различно кодиране и затова трябваше да бъдат променени.
Освен таблиците, всяка колона която съдържа кирилица трябва да има колация utf8_bin или utf8_general_ci.
Разликата между двете е, че utf8_bin сравнява символите като двоични данни и се ползва, когато искаме да различаваме малки и големи букви при сортиране и сравнение.
Ако ползваме utf8_general_ci, то при търсене в таблицата на 'Вада', ще излизат резултати като 'Вада', 'вада', 'ВАДА', 'ВаДа' и т.н. Затова най-често се ползва utf8_bin и за текстовите колони:
`name` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',
Ето защо аз трябваше да направя още редица замествания в .sql файла, за промяна на всички дефиниции на таблиците:
sed 's/CHARSET=latin1/CHARSET=utf8/g s/CHARSET=cp1251 COLLATE=cp1251_general_cs/CHARSET=utf8 COLLATE=utf8_bin/g s/CHARSET=cp1251 COLLATE=cp1251_general_ci/CHARSET=utf8 COLLATE=utf8_bin/g s/CHARSET=cp1251 COLLATE=cp1251_bulgarian_ci/CHARSET=utf8 COLLATE=utf8_bin/g s/character set latin1/character set utf8/g s/CHARACTER SET latin1/character set utf8/g s/collate latin1_bin/collate utf8_bin/g s/COLLATE latin1_bin/collate utf8_bin/g s/collate cp1251_bulgarian_ci/collate utf8_bin/g s/collate cp1251_bulgarian_cs/collate utf8_bin/g s/collate cp1251_general_cs/collate utf8_bin/g ' db-new.sql > db-utf8.sql
Тук вече във файл db-utf8.sql има описания на таблици и данни, които са в UTF-8. Самият файл db-utf8.sql също е в кодиране UTF-8. И новата база данни е създадена с колация UTF-8. Остава да се импортне файла в базата. Разбира се отново имаше проблем 😉 Той беше с големината на файла от около 270MB. Повечето хостинг доставчици имат ограничение от 50MB файл за импорт.
.
Следва продължение... (абонирай се за публикации от SEO блога)
Продължава в Промяна на енкодинга на сайт към UTF-8 (част 2)
Прочетена:11919