Pythonda unicode va kodlash jadvallari.
Salom. Manimcha ko'pchilik unicode haqida eshitgan bo'lsa kerak. Ushbu maqolam ham web dasturlashga kirib kelib, nimaga saytimda "ҲҒҚЎ" harflari o'rnida "?" belgisi chiqib qolyapti degan savollarni bergan dasturchilarga bag'ishlamoqchiman. Chunki bir paytlar o'zim ham shu savolni berganman.
Keling ozgina tarix. Dastlab kompyuter chiqqan paytlarda biz tanigan harflar bilan ishlashga yagona ASCII ishlab chiqilgan. Biz bilamizki ASCII jadvalida 256 ta belgi bor. Shundan 0-127 gacha bo'lgani o'zgarmas qilingan, qolgan 128-255 gacha bo'lganini har kim o'zicha o'zgartirib o'zlarining jadvalini yaratgan. Misol uchun windows-1251 ham ASCII ning davomiga qo'shilgan.
O'z-o'zidan ma'lumki bizning "Ғ" harfimiz bu kodlash jadvalida yo'q. Shunaqa jadvallarning ko'payishi tufayli, bitta kodlash jadvalida yozilgan matnni boshqa joyga uzatishda muammolar chiqishni boshlagan. Shunda barcha servislar (email, http, ...) larga qo'shimcha «Content-encoding» tushunchasini kiritishgan. Shu bilan muammoni hal qilishgan.
Kodlash jadvali — bu kompyuter xotirasidagi sonning qaysi belgiga to'g'ri kelishini ko'rsatuvchi jadval. Misol uchun: UTF-8 dagi "©" belgisi 2 bayt (0xc2 0xa9) ko'rinishida ifodalansa, windows-1251 jadvalida 1 bayt (0xA9) bilan ifodalanadi.
Demak kimdakim sayt qilgan bo'lsa va saytida "©" belgisini qo'ymoqchi bo'lsa:
ko'rinishida o'ziga keragini tanlashi lozim.
Keling endi quyidagi vaziyatni kuzatamiz. Siz sayt qilgansiz, sizdagi barcha ma'lumotlar UTF-8 da ishlaydi. Lekin qaysidir servisdan ma'lumot olishingiz kerak. U servis windows-1251 da ishlaydi. Shunda o'z-o'zidan muammo kelib chiqadi, windows-1251 da yozilgan ma'lumotni UTF-8 o'tkazish kerak degan? Agar o'tkazmasangiz, sizning saytingizda «Тошкент» so'zi "�������‚" ko'rinishida chiqadi. Chunki windows-1251 da har bir harf 1 baytdan keladi, UTF-8 da 2 bayt.
Shu yerga kelganda nechtadir aqlli boshlar birlashib UNICODE ni standartini tafsiya qilishgan. Unicode bu kodlash jadvali emas, ya'ni kompyuter xotirasidagi sonning qaysi belgiga to'g'ri kelishini ko'rsatmaydi.
Unicode bu — kodlash jadvallari aro o'tkazishni ta'minlaydigan standart. Ya'ni unicode ga yer yuzidagi barcha belgilarni kiritishadi va u belgiga qandaydir raqam berishadi. Agar shu belgi UTF-8 va windows-1251 da bo'lsa, UTF-8 dan Unicode ga, Unicode dan windows-1251 ga o'tkazishadi. Agar kodlash jadvallarida mavjud bo'lmagan belgilarni o'tkazish kerak bo'lib qolsa, ko'pincha o'rniga "?" belgisi chiqib qoladi.
Keling endi shu yozganimizni python misolida ko'rib chiqsak. Hozir ko'rsatmoqchi bo'lganlarim siz ishlab turgan OS ga ham bog'liq, chunki klaviaturadan krilcha ma'lumotni kiritishda OS da utf-8 ko'rsatilgan bo'lsa UTF-8 da, windows-1251 ko'rsatilgan bo'lsa windows-1251 da qilib yozadi. Shuning uchun man Ubuntu misolida ikkala holatda ham ko'rsataman.
Deyarli barcha dasturlash tillarida qator (string) lar bilan ishlaganda 2 ta tushuncha mavjud:
Encoded — kodlangan qator. Bu shuqday stringki, bundagi belgilar kodlangan jadval asosida aniqlanadi. Ya'ni string «utf-8 encoded» bo'lsa, demak «Hello Ғ» harfi «Hello \\xd2\\x92» ko'rinishida xotirada saqlanadi. UTF-8 da «d2 92» o'zimizning "Ғ" ga to'g'ri keladi.
Decoded — bu bayt array, ya'ni hali hech qanday ma'noni anglatmaydigan baytlar ketmaketligi. Agar o'sha "Ғ" ni olsak
ko'rinishdagi massiv bo'ladi. Agar buni UTF-8 bo'yicha encode qilsangiz "Ғ" ni olasiz, windows-1251 bo'yicha qilsangiz «Т’» ko'rinishidagi qatorni olasiz. Shunda "Ғ" UTF-8 encoded, «Т’» windows-1251 encoded qator deyiladi.
Hozirgi kunda dasturlash tillarida Unicode string (qator) lar mavjud, ya'ni C++ da barcha stringlar bayt ko'rinishida ifodalansa, pythonda unicode ko'rinishida ifodalanadi. Ya'ni decoded qator pythonda unicode turiga tegishli qator bo'ladi. Python3 da barcha decoded qatorlar unicodeda ifodalanadi, python2 da esa byte array yoki unicode turda ifodalanadi.
Agar shuni python3 da qilsangiz, ikkala holatda ham type 'unicode' natijani olasiz (sinab ko'rmadim).
Endi Ubuntuda terminalni ochib, kiritish encodingini UTF-8 ga qo'yamiz.

Buni qo'ysangiz, siz klaviaturadan kiritgan barcha belgilar UTF-8 encoded ya'ni UTF-8 belgi deb qabul qilishni boshlaydi.
Misol tariqasida krilchadagi "Ў" harfimizni olamiz, chunki bu belgi windows-1251 va UTF-8 da ham mavjud va "Ғ" ni olamiz, bu windows-1251 da mavjud emas.
Agar endi terminalni windows-1251 ga o'tkazib, "Ғ" ni kiritaman desangiz "?" belgisini yozadi, agar
e'tibor bergan bo'lsangiz bitta belgida ikki xil ko'rinishdagi qiymat chiqdi. Chunki kiritilgandagi kodlash jadvali har xil bo'lganligi uchun shu kodlash jadvaliga mos holdagi qiymatni oldik. Endi yana terminalni UTF-8 ga o'tkazib quyidagicha ishlarni qilib ko'ramiz.
«str».decode([kodlash jadvali]) — biz UTF-8 da «ОпенНет» deb yozdik (chunki kiritish UTF-8, terminalniki). decode funksiyasi esa, UTF-8 dagi qatorni unicode ga o'tkazadi. Ya'ni decoded qatorga o'tkazadi. Biz ushbu unicode qatordan foydalanib o'zimiz hohlagan kodlash jadvaliga o'tkazsak bo'ladi. Buning uchun
«str».encode([kodlash jadvali]) — bu unicode qatorni bizga zarur bo'lgan kodlash jadvaliga o'tkazadi.
Agar "\\u041e\\u043f\\u0435\\u043d\\u041d\\u0435\\u0442" qatorni unicode-table.com/ru/ servisidan qidirib har bir harfni ko'rib chiqsangiz biz yozgan «ОпенНет» harflariga to'g'ri keladi. Endi
ikkala holatda ham "\\xce\\xef\\xe5\\xed\\xcd\\xe5\\xf2" natijani oldik. Endi bu belgilarni ru.wikipedia.org/wiki/Windows-1251 jadvalidan ko'rib chiqsak yana biz yozgan «ОпенНет» so'ziga mos keladi.
\\x — o'n oltilikda berilgan qiymatni ko'rsatadi
\\u — unicode dagi belgini ko'rsatadi
Ko'pchilikda manashu holat rosa uchrasa kerak. Bu yerda muammo nimada, biz kiritgan «ОпенНет» so'zi UTF-8 da yozilgan, biz decode('windows-1251') qildik. Agar «ОпенНет» ni UTF-8 dagi byte kodini ko'rsangiz
natijani olasiz. Endi shuni decode('windows-1251') qilsak, har bir \\xd0, \\x92 larni windows-1251 jadvalidan foydalanib unicode ga o'tkazadi. Bunda 0xd0 — Р, 0x9e — ћ ga teng.
Umumiy qilib aytganda, kodlash jadvali bitta songa to'g'ri keladigan belgini aniqlashga kerak. Qolgani endi sizning fantaziyangizga bog'liq. Man umumiy terminalrni yozdim, agar savollar yoki tushunmovchiliklar bo'lsa kutaman.
Keling ozgina tarix. Dastlab kompyuter chiqqan paytlarda biz tanigan harflar bilan ishlashga yagona ASCII ishlab chiqilgan. Biz bilamizki ASCII jadvalida 256 ta belgi bor. Shundan 0-127 gacha bo'lgani o'zgarmas qilingan, qolgan 128-255 gacha bo'lganini har kim o'zicha o'zgartirib o'zlarining jadvalini yaratgan. Misol uchun windows-1251 ham ASCII ning davomiga qo'shilgan.
O'z-o'zidan ma'lumki bizning "Ғ" harfimiz bu kodlash jadvalida yo'q. Shunaqa jadvallarning ko'payishi tufayli, bitta kodlash jadvalida yozilgan matnni boshqa joyga uzatishda muammolar chiqishni boshlagan. Shunda barcha servislar (email, http, ...) larga qo'shimcha «Content-encoding» tushunchasini kiritishgan. Shu bilan muammoni hal qilishgan.
Kodlash jadvali — bu kompyuter xotirasidagi sonning qaysi belgiga to'g'ri kelishini ko'rsatuvchi jadval. Misol uchun: UTF-8 dagi "©" belgisi 2 bayt (0xc2 0xa9) ko'rinishida ifodalansa, windows-1251 jadvalida 1 bayt (0xA9) bilan ifodalanadi.
Demak kimdakim sayt qilgan bo'lsa va saytida "©" belgisini qo'ymoqchi bo'lsa:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> // UTF-8 uchun
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" /> //windows-1251 uchun
ko'rinishida o'ziga keragini tanlashi lozim.
Keling endi quyidagi vaziyatni kuzatamiz. Siz sayt qilgansiz, sizdagi barcha ma'lumotlar UTF-8 da ishlaydi. Lekin qaysidir servisdan ma'lumot olishingiz kerak. U servis windows-1251 da ishlaydi. Shunda o'z-o'zidan muammo kelib chiqadi, windows-1251 da yozilgan ma'lumotni UTF-8 o'tkazish kerak degan? Agar o'tkazmasangiz, sizning saytingizda «Тошкент» so'zi "�������‚" ko'rinishida chiqadi. Chunki windows-1251 da har bir harf 1 baytdan keladi, UTF-8 da 2 bayt.
Shu yerga kelganda nechtadir aqlli boshlar birlashib UNICODE ni standartini tafsiya qilishgan. Unicode bu kodlash jadvali emas, ya'ni kompyuter xotirasidagi sonning qaysi belgiga to'g'ri kelishini ko'rsatmaydi.
Unicode bu — kodlash jadvallari aro o'tkazishni ta'minlaydigan standart. Ya'ni unicode ga yer yuzidagi barcha belgilarni kiritishadi va u belgiga qandaydir raqam berishadi. Agar shu belgi UTF-8 va windows-1251 da bo'lsa, UTF-8 dan Unicode ga, Unicode dan windows-1251 ga o'tkazishadi. Agar kodlash jadvallarida mavjud bo'lmagan belgilarni o'tkazish kerak bo'lib qolsa, ko'pincha o'rniga "?" belgisi chiqib qoladi.
Keling endi shu yozganimizni python misolida ko'rib chiqsak. Hozir ko'rsatmoqchi bo'lganlarim siz ishlab turgan OS ga ham bog'liq, chunki klaviaturadan krilcha ma'lumotni kiritishda OS da utf-8 ko'rsatilgan bo'lsa UTF-8 da, windows-1251 ko'rsatilgan bo'lsa windows-1251 da qilib yozadi. Shuning uchun man Ubuntu misolida ikkala holatda ham ko'rsataman.
Deyarli barcha dasturlash tillarida qator (string) lar bilan ishlaganda 2 ta tushuncha mavjud:
Encoded — kodlangan qator. Bu shuqday stringki, bundagi belgilar kodlangan jadval asosida aniqlanadi. Ya'ni string «utf-8 encoded» bo'lsa, demak «Hello Ғ» harfi «Hello \\xd2\\x92» ko'rinishida xotirada saqlanadi. UTF-8 da «d2 92» o'zimizning "Ғ" ga to'g'ri keladi.
Decoded — bu bayt array, ya'ni hali hech qanday ma'noni anglatmaydigan baytlar ketmaketligi. Agar o'sha "Ғ" ni olsak
data = [0xd2, 0x92]
ko'rinishdagi massiv bo'ladi. Agar buni UTF-8 bo'yicha encode qilsangiz "Ғ" ni olasiz, windows-1251 bo'yicha qilsangiz «Т’» ko'rinishidagi qatorni olasiz. Shunda "Ғ" UTF-8 encoded, «Т’» windows-1251 encoded qator deyiladi.
Hozirgi kunda dasturlash tillarida Unicode string (qator) lar mavjud, ya'ni C++ da barcha stringlar bayt ko'rinishida ifodalansa, pythonda unicode ko'rinishida ifodalanadi. Ya'ni decoded qator pythonda unicode turiga tegishli qator bo'ladi. Python3 da barcha decoded qatorlar unicodeda ifodalanadi, python2 da esa byte array yoki unicode turda ifodalanadi.
>>> type("text")
<type 'str'>
>>> type(u"text")
<type 'unicode'>
Agar shuni python3 da qilsangiz, ikkala holatda ham type 'unicode' natijani olasiz (sinab ko'rmadim).
Endi Ubuntuda terminalni ochib, kiritish encodingini UTF-8 ga qo'yamiz.

Buni qo'ysangiz, siz klaviaturadan kiritgan barcha belgilar UTF-8 encoded ya'ni UTF-8 belgi deb qabul qilishni boshlaydi.
Misol tariqasida krilchadagi "Ў" harfimizni olamiz, chunki bu belgi windows-1251 va UTF-8 da ham mavjud va "Ғ" ni olamiz, bu windows-1251 da mavjud emas.
>>> "Ў"
'\\xd0\\x8e'
Agar endi terminalni windows-1251 ga o'tkazib, "Ғ" ni kiritaman desangiz "?" belgisini yozadi, agar
>>> "Ў"
'\\xa1'
e'tibor bergan bo'lsangiz bitta belgida ikki xil ko'rinishdagi qiymat chiqdi. Chunki kiritilgandagi kodlash jadvali har xil bo'lganligi uchun shu kodlash jadvaliga mos holdagi qiymatni oldik. Endi yana terminalni UTF-8 ga o'tkazib quyidagicha ishlarni qilib ko'ramiz.
>>> "ОпенНет".decode('utf-8')
u'\\u041e\\u043f\\u0435\\u043d\\u041d\\u0435\\u0442'
«str».decode([kodlash jadvali]) — biz UTF-8 da «ОпенНет» deb yozdik (chunki kiritish UTF-8, terminalniki). decode funksiyasi esa, UTF-8 dagi qatorni unicode ga o'tkazadi. Ya'ni decoded qatorga o'tkazadi. Biz ushbu unicode qatordan foydalanib o'zimiz hohlagan kodlash jadvaliga o'tkazsak bo'ladi. Buning uchun
«str».encode([kodlash jadvali]) — bu unicode qatorni bizga zarur bo'lgan kodlash jadvaliga o'tkazadi.
Agar "\\u041e\\u043f\\u0435\\u043d\\u041d\\u0435\\u0442" qatorni unicode-table.com/ru/ servisidan qidirib har bir harfni ko'rib chiqsangiz biz yozgan «ОпенНет» harflariga to'g'ri keladi. Endi
>>> "ОпенНет".decode('utf-8').encode('windows-1251')
'\\xce\\xef\\xe5\\xed\\xcd\\xe5\\xf2'
>>> u'\\u041e\\u043f\\u0435\\u043d\\u041d\\u0435\\u0442'.encode('windows-1251')
'\\xce\\xef\\xe5\\xed\\xcd\\xe5\\xf2'
ikkala holatda ham "\\xce\\xef\\xe5\\xed\\xcd\\xe5\\xf2" natijani oldik. Endi bu belgilarni ru.wikipedia.org/wiki/Windows-1251 jadvalidan ko'rib chiqsak yana biz yozgan «ОпенНет» so'ziga mos keladi.
\\x — o'n oltilikda berilgan qiymatni ko'rsatadi
\\u — unicode dagi belgini ko'rsatadi
>>> print "ОпенНет".decode('windows-1251')
ОпенНет
Ko'pchilikda manashu holat rosa uchrasa kerak. Bu yerda muammo nimada, biz kiritgan «ОпенНет» so'zi UTF-8 da yozilgan, biz decode('windows-1251') qildik. Agar «ОпенНет» ni UTF-8 dagi byte kodini ko'rsangiz
>>> "ОпенНет"
'\\xd0\\x9e\\xd0\\xbf\\xd0\\xb5\\xd0\\xbd\\xd0\\x9d\\xd0\\xb5\\xd1\\x82'
natijani olasiz. Endi shuni decode('windows-1251') qilsak, har bir \\xd0, \\x92 larni windows-1251 jadvalidan foydalanib unicode ga o'tkazadi. Bunda 0xd0 — Р, 0x9e — ћ ga teng.
Umumiy qilib aytganda, kodlash jadvali bitta songa to'g'ri keladigan belgini aniqlashga kerak. Qolgani endi sizning fantaziyangizga bog'liq. Man umumiy terminalrni yozdim, agar savollar yoki tushunmovchiliklar bo'lsa kutaman.
Нет комментариев