UTF-8: Boshidan-oxirigacha! 2-qism! utf8_strlen!
Agar UTF-8 haqida qisqacha ma'lumot olmoqchi bo'lsangiz, 1-qismga o'ting.
Agar 1-maqolani o'qigan bo'lsangiz, UTF-8 da belgilar 1 baytdan 4 baytgacha bo'lishi mumkin deb o'tganman. Agar shunday bo'lsa, "AЩぁ𐄳" qator (string) uzunligi nechchi bo'lishi kerak?
#include <iostream> #include <locale> #include <string> int main() { std::string str{"AЩぁ𐄳"}; std::cout<<"LENGTH = "<<str.length()<<std::endl; return 0; }
Dastur natijasi:
LENGTH = 10
Bizga esa 4 kerak.
Keling oldin UNICODE dagi belgi UTF-8 ga qanday tartibda o'tkazilishini o'rgnaib chiqsak. U 3 da qadamdan iborat.
1-qadam: UNICODE dagi belgilarni saqlashga kerak bo'ladigan BAYT lar sonini aniqlash, quyidagi jadval asosida bo'ladi.
BELGILAR ORALIG'I (UNICODE) | KERAK BO'LADIGAN BAYTLAR SONI |
00000000-0000007F | 1 |
00000080-000007FF | 2 |
00000800-0000FFFF | 3 |
00010000-0010FFFF | 4 |
Ya'ni, agar biz UNICODE dagi U+0080 belgini UTF-8 da saqlamoqchi bo'lsak, bizga 2 bayt kerak bo'ladi.
2-qadam: Kerak bo'lgan baytlar sonidan kelib chiqib, quyidagi jadval asosida eng birinchi turgan baytning bit qiymatlarini o'zgartirish kerak.
0xxxxxxx | agar 1 bayt bo'lsa |
110xxxxx | agar 2 bayt bo'lsa |
1110xxxx | agar 3 bayt bo'lsa |
11110xxx | agar 4 bayt bo'lsa |
1 tadan ko'p bo'lgan holatda, keyingi baytlarning boshida har doim 10 (ikkilikda) bo'lishi lozim. Umuman olganda, baytlar sonidan kelib chiqib quyidagi jadvalni shablon sifatida olsak bo'ladi.
Baytlar soni | Kerakli bitlar | Shablon |
1 | 7 | 0xxxxxxx |
2 | 11 | 110xxxxx 10xxxxxx |
3 | 16 | 1110xxxx 10xxxxxx 10xxxxxx |
4 | 21 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
xxxx - bular UNICODE dagi qiymatni saqlashga ishlatiladigan joylar (bit ko'rinishida).
3-qadam: UNICODE dagi birorta belgini olib, uning qiymatidan kelib chiqib keraklicha bayt uzunlikni tanlash kerak va xxxx larning o'rniga UNICODE dagi belgi qiymatini joylashtirish kerak. Ortib qolgan joylar esa 0 bilan to'ldiriladi.
Yuqoridagi 3-da qadam bu tarjima (wikipediadan). Endi keling, misol tariqasida ketma-ket tushuntirsam. Sal murakkab bo'lsada, bir vaqtning o'zida 4 ta belgi bilan ishlasak, manimcha tushinish oson bo'ladi.
Belgilar (UNICODE da):
IZOH\BELGI | A | Щ | ぁ | 𐄳 |
UNICODE dagi kodi | U+0041 | U+0429 | U+3041 | U+10133 |
Kerak bo'ladigan baytlar soni | 1 [00000000-0000007F] oraliqda | 2 [00000080-000007FF] oraliqda | 3 [00000800-0000FFFF] oraliqda | 4 [00010000-0010FFFF] oraliqda |
Ikkilik sanoq sistemada ko'rinishi | 1000001 | 10000101001 | 11000001000001 | 10000000100110011 |
2-qadamdagi shablonlardan foydalanib "x" larga mos uzunlikka ajratsak, yuqoridagi ikkilikdagi qiymatlarini:
0xxxxxxx 1000001 110xxxxx 10xxxxxx 10000 101001 1110xxxx 10xxxxxx 10xxxxxx 11 000001 00000 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 10000 000100 110011
IZOH\BELGI | A | Щ | ぁ | 𐄳 |
Yuqoridagini mox ravizda x lar o'rniga qiymatlarni joylashtiramiz, yetmay qolganiga 0 qo'yamiz | 01000001 | 11010000 10101001 | 11100011 10000001 10000001 | 11110000 10010000 10000100 10110011 |
Endi shu 2 likdagi qiymatlarni 16 likda yozamiz. | 0x41 | 0xD0A9 | 0xE38181 | 0xF09084B3 |
UTF-8 dagi natijalar esa quyidagicha:
A - U+0041 = 0x41
Щ - U+0429 = 0xD0A9
ぁ - U+3041 = 0xE38181
𐄳 - U+10133 = 0xF09084B3
Demak xulosa: Agar baytning boshidagi bitlari mos ravishda:
0 bo'lsa, demak u 1 baytli UTF-8 belgi.
110 bo'lsa, demak 2 baytli belgi
1110 bo'lsa demak, 3 baytli belgi
11110 bo'lsa, demak 4 baytli belgi.
Shu qoidadan kelib chiqib, utf8_strlen funksiyamiz quyidagi ko'rinishda bo'ladi.
#include <iostream> #include <locale> #include <string> int utf8_strlen(const std::string& s) { int len = 0; int i = 0; while(i < s.length()) { char c = s.at(i); if ((c & 0xF8) == 0xF0) {// 0xF8 = 1111 1000, 0xF0 = 1111 0000 i += 4; } else if ((c & 0xF0) == 0xE0) { //0xE0 = 1110 0000 i += 3; } else if ((c & 0xE0) == 0xC0) { //0xC0 = 1100 0000 i += 2; } else { //if ((c & 0x80) == 0) { //0x80 = 1000 0000 i++; } len++; } return len; } int utf8_strlen2(const std::string& s) { int len = 0; for(int i = 0; i < s.length(); i++) { char c = s.at(i); if ((c & 0xC0) != 0x80) { //0xC0 = 1100 0000, 0x80 = 1000 0000 len++; } } return len; } int main() { std::string str{"AЩぁ𐄳"}; std::cout<<"LENGTH = "<<str.length()<<std::endl; std::cout<<"UTF8 LENGTH = "<<utf8_strlen(str)<<std::endl; std::cout<<"UTF8 LENGTH2 = "<<utf8_strlen2(str)<<std::endl; return 0; }
Natija:
LENGTH = 10 UTF8 LENGTH = 4 UTF8 LENGTH2 = 4
Eslatma: utf8_strlen funksiya utf8_strlen2 ga nisbatan tez ishlaydi, chunki utf8_strlen da birinchi bayt tekshirilib qolganlari shundoq tashlab ketiladi. Ikkinchi funksiyada esa, bayt boshi 10 dan (ikkilikda) boshlaganlarni tekshiriladi. Shuning uchun ikkinchi funksiya 0 dan s.length() gacha to'liq aylanadi.
Manimcha bugunga yetarli.
Birinchi bo‘ling!