От японски „moji“ – символ, буква, и „bakeru“ – деформирам, окепазявам.
Да си припомним, че браузърът или по-общо казано web client-ът получава respons-ът от сърва като „купчина единици и нули“. Която купчина единици и нули, той трябва да знае как да я визуализира, и изобщо как да я използва.
По принцип сървът изпраща в отговорът на даденият рекуест, като част и HTTP хедърът Content-Type, с който показва на клиента за какъв тип контент иде реч – текст, изображение…
Ако е изображение ОК, клиентът ще го визуализира, стига да знае графичният формат – JPEG, GIF, PNG…, за което трябва също да има съответният HTTP хедър Content-Type.
Но ако е текст, правилното визуализиране зависи и от това клиентът да знае с какъв енкодинг е кодиран въпросният текст. В смисъл, че една и съща буква, например латинско „W“ или кирилско „Щ“, може в един енкодинг да има един двоичен код, в друг енкодинг – друг, и т.н. „глиф“ (самата буква) един път да е една, друг път на… на Бог знае каква азбука.
Ако се случи ситуация клиентът да декодне (демек визуализира) въпросният текст с грешният енкодинг, това е mojibake, демек – т.н. „гарги“ или текст, написан уж на една азбука и език, а показан на друга.
Затова понякога се случва текст например на български и на кирилица, да излиза на „китаица“, „японица“ или „аборигеница“, и трябва в менюто на браузъра да задаваме енкодинга ръчно.
strlen() брои броя байтове, които даденият стринг заема, което значи, че би върнала правилен резултат за енкодинги като ASCII, ANSI, UTF-8…
strlen() returns the number of bytes rather than the number of characters in a string.
mb_strlen() брои броя символи, но трябва да знае какъв на енкодинг е даденият стринг, за да знае как да ги преброи. If it is omitted or null, the internal character encoding value will be used.If the encoding is unknown, an error of level E_WARNING is generated.
Според Joel Spolsky тагът <meta http-equiv=“Content-Type“ content=“text/html; charset=……“> трябва да е веднага след <head>, защото така браузърът ще спре да парсира HTML кода до там и ще започне отначало, използвайки зададеният енкодинг.
Демек, вече ще знае с какъв енкодинг да дисплейне дадената страница. Да не забравяме, че браузърът получава „купчина единици и нули“, как да знае точно от кой енкодинг са.
But that meta tag really has to be the very first thing in the <head> section because as soon as the web browser sees this tag it’s going to stop parsing the page and start over after reinterpreting the whole page using the encoding you specified.
Aко нито с HTTP хедър, нито с този HTML таг не е зададен енкодингът на страницата, браузърите имат свои механизми да разгадаят правилният енкодинг, но едва ли безгрешно. И едва ли бързо…
Е да, другият вариант е с HTTP тагът Content-Type:
През 60-те години на XX век липсва общ стандарт за кодиране на символи и всеки производител на хардуер използва собствени кодове за символите.
Боб Бъмър – програмист в IBM, наричан „бащата на ASCII“ – обръща внимание, че с използването на различни стандарти, машините на IBM не могат да комуникират помежду си, а още по-малко с машините на останали производители. През май 1961 г. той предлага на Американския институт за стандартизация да се въведе и използва общ стандарт между компютрите. За работа по проекта се създава подкомитет X3.2, съставен от представители на повечето производители на компютри. Всеки от производителите настоява да запази своя начин на представяне на символите. Повече от две години са нужни на комитета, за да постигнат съгласие за един общ код.
ASCII е 7-битов код (character encoding), което означава, че може да представи 128 символа, 33 от които – непечатаеми (които служат за управление на устройства за обработка на текст) и съдържа само латински букви, арабски цифри и някои специални символа като пункуационни такива и т.н…
ASCII определя еднозначно съответствие между двоични кодове и писмени знаци (глифи), правейки възможна обмяната на текстова информация между отделни цифрови устройства, както и нейното съхраняване в тези устройства. Важно е да се отбележи, че ASCII определя съответствие между кода и семантичната стойност на глифа, а не негова конкретна визуална реализация (тоест, как точно изглежда дадената буква, цифра или друг знак).
Компютърните шрифтове определят визуалната реализациите на глифите. Тоест, ASCII няма нищо общо с това как точно, с какъв шрифт, даденият символ ще бъде изобразен.
Да вземем за пример главната латинска буква A – нейният двоичен код е 0100 0001, което е 65 в десетичен код или 41 в шестнайсетичен. В един обикновен текстов файл в ASCII код тя ще бъде представена именно с този код – 65(10) или 41(16). Но за да се изобрази на екрана, е нужен шрифт, който от своя страна съпоставя на този код някакво изображение. Това изображение е различно при отделните шрифтове, но смисълът му е един и същ – главна латинска буква A.
ANSI
Когато, с времето възниква нужда от покриване на повече писмени знаци (глифи), ASCII прераства в ANSI чрез добавяне на още един бит – старши бит, от ляво.
Това удвоява броя на възможните символи, тоест ако до тогава в ASCII кодът с най-голяма числова стойностт е бил: 0111 1111 демек 127(10) сега вече щом ще използваме и най-старшият бит от този байт, ще започнем от: 1000 0000 демек 128(10) и т.н… до 1111 1111 демек 255(10)
Това открива възможността да можем да удвоим броя възможни символни кода, които да съхраним.
Досегашните 128 символа от ASCII си остават, просто ги допълваме с още толкова.
Което е половината новина, другата половина е, че всеки има различни идеи кои точно да са тези допълнителни символи. Така възникват т.н. „code pages“ – това са тези различни, отделни „допълнения“ от по 128 символа.
Тези различни code pages най-често са на национален принцип, демек – имат цел да покрият даден език или по-скоро азбука – гръцки, еврейски, кирилица…
Просто казано, всички code pages си приличат само в първата половина. От там нататък… всичко е специфично за дадената code page.
In the ANSI standard, everybody agreed on what to do below 128, which was pretty much the same as ASCII, but there were lots of different ways to handle the characters from 128 and on up, depending on where you lived. These different systems were called code pages.
The main difference between ANSI and ASCII is the number of characters they can represent. ASCII was the first to be developed and when its limitations were reached, ANSI was one of the ways created to expand the number of characters that can be represented in an encoding, increasing the maximum number of characters to be represented up to 256. ASCII just defines a 7 bit code page with 128 symbols. ANSI extends this to 8 bit and there are several different code pages for the symbols 128 to 255.
Eто от този пример: https://en.wikipedia.org/wiki/Windows-1252 се вижда ясно как от този конкретен codepage (Windows-1252), ASCII таблицата е само първата половина, след това започват конкретните за него Windows-1252 codepage.
За друг codepage имаме подобна картина: https://en.wikipedia.org/wiki/Code_page_932_(Microsoft_Windows)
Изводът е, че всеки codepage сам за себе си дефинира кои ще са символите, с кодове по-големи от 127 (10), стига само кодовете от 0 до 127 да са за същите символи като на ASCII.
Добър въпрос би бил, ОК имаме даден текстов файл или дадена web страница, кодирана в даден codepage. Kaк да знаем кой е codepage и за да я визуализираме? До колкото знам, файловете в хедърите си имат такава информация. За web страниците – там имаме специален HTTP header Content-Type: text/html; charset=UTF-8 както и HTML мета таг.
Друго просто и важно е да се знае, че няма такъв eнкодинг наречен ANSI. ANSI e просто концепцията (даже по-скоро името на организацията) за това как да се удвои броя символни кода като се използва и най-старшият байт. За да кажеш, че даден файл например е кодиран с ANSI трябва да знаеш с коя конкретно code page е кодиран, защото всичко над 127(10) зависи от това.
Unicode
С времето и най-вече навлизането на Интернет, се вижда, че и 8 бита са крайно недостатъчни, защото има азбуки с хиляди букви (глифи). Така възниква Unicode.
Има един широко разпространен мит, че всеки Unicode символ е 16 битов код и следователно Unicode може да поддържа максимално 65,536 символа. Това не е точно така.
На всеки Unicode символ е зададен уникален т.н. „code point“ напр. U+0041 за латинско А. U+ значи Unicode, а двата байта след него са самият code point, който е шестнайсетично число. Няма лимит за броя символи, обхванати от Unicode, отделно, те отдавна са надхвърлили 65,536 броя.
Как обаче този code point се съхранява в паметта?
Да вземем стрингът Hello В Unicode той е пет code points U+0048 U+0065 U+006C U+006C U+006F Което в паметта е 00 48 00 65 00 6C 00 6C 00 6F Ясно се виждат двойките байтове за всеки символ.
Meжду другото така е и била първоначалната идея за Unicode – всичко да е по два байта и толкоз. Явно оттам е тръгнал и митът, че в Unicode всеки символ е винаги точно 2 байта.
Тук също идва и едно понятие, наречено endianness или байтова подредба, според което понятие ако имаме little endian 00 48 ще е 48 00, ако имаме big endian ще си e 00 48. Което довело до нуждата предварително да се знае какъв е endianness-ът на даден стринг. Измислили т.н. Unicode Byte Order Mark пред всеки стринг и можел да бъде или FE FF или FF FE, но това била неудачна идея.
Така или иначе, два байта си трябват, само че реално, в повечето случаи (напр. английски и други латински езици като латинско A) и един байт е напълно достатъчен.
„Много нули бре!“ скочили разни люде, и правилно, ако е за латински букви, си е загуба на един излишен байт за всяка. Пък и отделно, всичко написано до тогава на ASCII и ANSI как да се преправи на Unicode?
Изглеждало, че дните на Unicode са преброени.
И тук се родил UTF-8. При него, ако кодът на символа е между 0 и 127 (застъпване с доброто старо ASCII) използваме ЕДИН байт, не два. Явно това значи 8 в UTF-8, демек колко минимално памет може да се заделя за всеки символ. Вече, за code points над 127 се заделят 2, 3, 4, 5 до 6 байта. Това е идеята на UTF-8 – да е по-пестелив откъм сторидж, демек, ако може един символ да се представи с 1 байт, да не се представя с 2. Kоето също значи, че латинските знаци например съвпадат 1 към 1 с ASCII и ANSI събратята си.
И ето няколко начина за кодиране в Unicode:
Традиционният, двубайтов начин, още познат като UCS-2 или UTF-16. Тук обаче е важен endianness, подредбата на байтовете. Демек може да имаме big endian UTF-16 (UCS-2) или little endian UTF-16 (UCS-2). Ако няма BOM се разбира по подразбиране big-endian.
UTF-8
UTF-7 – същото като UTF-8 но старшият бит на всеки байт трябва да е 0
UTF-8 and UTF-16 both handle the same Unicode characters. They are both variable length encodings that require up to 32 bits per character. The difference is that UTF-8 encodes the common characters including English and numbers using 8-bits. UTF-16 uses at least 16-bits for every character.
UTF-16 и UTF-32 са несъвместими с ASCII, за разлика от UTF-8.
UTF-8 requires 8, 16, 24 or 32 bits (one to four bytes) to encode a Unicode character, UTF-16 requires either 16 or 32 bits to encode a character, and UTF-32 always requires 32 bits to encode a character.