HTML與javascript常碰到的編碼問(wèn)題

2023-12-30    分類(lèi): 網(wǎng)站建設(shè)

在日常的前端開(kāi)發(fā)工作中,我們會(huì)經(jīng)常的與HTML、javascript、css等語(yǔ)言打交道,和一門(mén)真正的語(yǔ)言一樣,計(jì)算機(jī)語(yǔ)言也有它的字母表、語(yǔ)法、詞法、編碼方式等,在這里我簡(jiǎn)單的談一下前端HTML與javascript日常工作中常碰到的編碼問(wèn)題。

在計(jì)算機(jī)中,我們儲(chǔ)存的信息都是用二進(jìn)制碼表示的。我們認(rèn)識(shí)的、屏幕上顯示的英文、漢字等符號(hào)和儲(chǔ)存用的二進(jìn)制代碼的互相轉(zhuǎn)換,就是編碼。

有兩個(gè)基本概念需要說(shuō)明,charset 和 character encoding:

charset?,字符集,也就是某個(gè)符號(hào)和某個(gè)數(shù)字映射關(guān)系的一個(gè)表,也就是它決定了107 是koubei 的 ‘a(chǎn)’,21475 是口碑的“口”,不同的表有不同的映射關(guān)系,如 ascii,gb2312,Unicode. 通過(guò)這個(gè)數(shù)字和字符的映射表,我們可以把一個(gè)二進(jìn)制表示的數(shù)字轉(zhuǎn)換成某個(gè)字符。 chracter encoding?,編碼方式。例如,同是對(duì)于應(yīng)“口”的 21475 這個(gè)數(shù),我們是用 \u5k3e3 表示呢,還是用 %E5%8F%A3 來(lái)表示呢?這就是由 character encoding 來(lái)決定的。

對(duì)于 ‘koubei.com’ 這樣的 字符串來(lái)說(shuō),是美國(guó)人的常用字符,他們就制定了一個(gè) 叫做ASCII 的字符集,全稱(chēng)是 american standard code of information interchange 美國(guó)標(biāo)準(zhǔn)信息交換碼,用0–127這128個(gè)數(shù)字,(2的7次方,0×00-0×7f) 代表了123abc這樣的常用的128個(gè)字符。一共是 7 bits,再加上第一個(gè)是符號(hào)位,要用來(lái)去補(bǔ)碼反碼表示負(fù)數(shù)什么的,一共8 bits 構(gòu)成一個(gè) byte。當(dāng)年美國(guó)人就是小氣了點(diǎn),要是一開(kāi)始就設(shè)計(jì)成一個(gè) byte 是16 bits、32 bits,世界上會(huì)少很多問(wèn)題,不過(guò)當(dāng)時(shí),估計(jì)他們覺(jué)得 8 bits 就夠了,可以表示128個(gè)不同的字符呢!

介于計(jì)算機(jī)這玩意兒是美國(guó)人搞出來(lái)的,所以他們自己省事,把自家用的符號(hào)都編碼好了,用的挺爽的。但當(dāng)計(jì)算機(jī)開(kāi)始國(guó)際化的時(shí)候,問(wèn)題出來(lái)了,拿中國(guó)舉例吧,漢字就好幾萬(wàn),怎么辦?

現(xiàn)有的 8 bits 一個(gè) byte 的系統(tǒng)是基礎(chǔ),不能破壞,不能去改到 16 bits之類(lèi)的,否則改動(dòng)太大了,只能走另一條路:用多個(gè) ascii 的字符去表示一個(gè)其他字符,也就是 MBCS ( Multi-Byte Character System,多字節(jié)字符系統(tǒng))。 有了這個(gè) MBCS 的概念,我們可以表示更多個(gè)字符了,比如我們用 2 個(gè) ascii 字符,就有 16 bits, 理論上有 2 的 16 次方 65536 個(gè)字符。但這些編碼怎么分配到字符上呢?比如口碑的”口”的 Unicode 編碼就是 21475,誰(shuí)決定的呢?字符集,也就是剛剛介紹的charset。ascii就是最基礎(chǔ)的一個(gè)字符集,在此之上,我們有類(lèi)似于 gb2312, big5這樣針對(duì)簡(jiǎn)體中文和繁體中文的MBCS的字符集等等。終于有個(gè)叫 Unicode Consortium 的機(jī)構(gòu),決定做一個(gè)囊括所有字符在內(nèi)的字符集(UCS, Universal Character Set)和對(duì)應(yīng)編碼方式的標(biāo)準(zhǔn),即 Unicode。從1991年開(kāi)始,它發(fā)布了第一版 Unicode 國(guó)際標(biāo)準(zhǔn),ISBN 0-321-18578-1 ,國(guó)際標(biāo)準(zhǔn)化組織 ISO 也參與了這個(gè)的定制,ISO/IEC 10646 : the Universal Character Set??傊琔nicode 是個(gè)基本覆蓋了所有已經(jīng)存在的地球上的符號(hào)的字符標(biāo)準(zhǔn)了,現(xiàn)在正在被越來(lái)越廣泛的使用,ECMA 標(biāo)準(zhǔn)也規(guī)定,javascript語(yǔ)言的內(nèi)部字符使用 Unicode 標(biāo)準(zhǔn)(這意味著,javascript的變量名、函數(shù)名等是允許中文的?。?br />
對(duì)于身在中國(guó)的開(kāi)發(fā)者來(lái)說(shuō),可能碰到比較多的問(wèn)題就是 gbk,? gb2312, utf-8 之間轉(zhuǎn)換之類(lèi)的問(wèn)題了。嚴(yán)格的說(shuō)這個(gè)說(shuō)法不是很準(zhǔn)確,gbk,gb2312是字符集 (charset),而 utf-8 是一種編碼方式 (character encoding) ,是 Unicode 標(biāo)準(zhǔn)中 UCS 字符集的一種編碼方式,因?yàn)槭褂?Unicode 字符集的網(wǎng)頁(yè)主要用UTF-8編碼,所以大家常常就把它們并列了,其實(shí)是不準(zhǔn)確的。

有了 Unicode 后,至少人類(lèi)文明沒(méi)有碰到外星人之前,這是一把萬(wàn)能鑰匙了,都用它吧。而現(xiàn)在使用最廣泛 Unicode 的編碼方式就是 UTF-8 (8-bit UCS/Unicode Transformation Format) 了,它有幾個(gè)特別好的地方:
  1. 編碼 UCS 字符集,全世界通用
  2. 是一種變長(zhǎng)編碼方式(variable-length character encoding),兼容 ascii
第二點(diǎn)是個(gè)很大的優(yōu)點(diǎn),它使得以前使用純 ascii 編碼的系統(tǒng)兼容,而且不會(huì)增加額外的存儲(chǔ)量(假設(shè)定長(zhǎng)的編碼方式,規(guī)定每個(gè)字符由2個(gè) bytes 組成,那么這時(shí)候 ascii 字符占用的存儲(chǔ)空間將增大一倍)。

要把 UTF-8 說(shuō)清楚,引入一個(gè)表會(huì)更方便了:

U-00000000 – U-0000007F:??? 0xxxxxxx U-00000080 – U-000007FF:??? 110xxxxx 10xxxxxx U-00000800 – U-0000FFFF:??? 1110xxxx 10xxxxxx 10xxxxxx U-00010000 – U-001FFFFF:??? 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx U-00200000 – U-03FFFFFF:??? 111110xx 10xxxxxx 10xxxxxx 10xxxxxx? 10xxxxxx U-04000000 – U-7FFFFFFF:??? 1111110x 10xxxxxx 10xxxxxx 10xxxxxx? 10xxxxxx 10xxxxxx

要看懂這個(gè)表呢,我們看前兩行就夠了

U-00000000 – U-0000007F: 0xxxxxxx 第一行是這樣的,意思是說(shuō),如果你發(fā)現(xiàn)一個(gè)utf-8編碼的 byte 的二進(jìn)制碼是0xxxxxxx,是0開(kāi)頭的, 即十進(jìn)制的0-127之間,那么他就是單獨(dú)的這一 byte 代表一個(gè)字符,而且是擁有和 ascii 碼完全一樣的含義。其他所有的 utf8 編碼的二進(jìn)制值都是用1開(kāi)頭的1xxxxxxx,大于127的,而且都需要至少2 bytes才能代表一個(gè)符號(hào)。所以一個(gè)字節(jié)的第一位是一個(gè)開(kāi)關(guān),代表這個(gè)字符是不是一個(gè) ascii 碼。這個(gè)就是剛才談到的兼容性,從英文定義上看,就是utf8編碼的兩個(gè)屬性:

UCS characters U+0000 to U+007F (ASCII) are encoded simply as bytes 0×00 to 0×7F (ASCII compatibility). This means that files and strings which contain only 7-bit ASCII characters have the same encoding under both ASCII and UTF-8. All UCS characters >U+007F are encoded as a sequence of several bytes, each of which has the most significant bit set. Therefore, no ASCII byte (0×00-0×7F) can appear as part of any other character.

然后我們看看第二行:

U-00000080 – U-000007FF:??? 110xxxxx 10xxxxxx 先看第一個(gè)字節(jié):110xxxxx,它的含義是,我不是一個(gè) ascii 碼(因?yàn)榈谝晃徊粸?),我是一個(gè)多 bytes 字符的第一個(gè) byte (第二位為1),我參與表示的這個(gè)字符是由2個(gè) bytes 組成的(第三位為0),從第四位開(kāi)始,就是字符的信息儲(chǔ)存的位置。 再看第二個(gè)字節(jié):10xxxxxx,它的含義是:我不是一個(gè) ascii 碼(因?yàn)榈谝晃徊粸?),我不是一個(gè)多 bytes 字符的第一個(gè) byte (第二位為0),第三位開(kāi)始是字符的信息儲(chǔ)存的位置。

從這個(gè)例子中可以總結(jié)出來(lái),utf-8編碼方式中,在一長(zhǎng)串連續(xù)的二進(jìn)制 byte 碼中,可能由2個(gè)至6個(gè) bytes 來(lái)表示一個(gè)符號(hào),那么相比較于用一個(gè) byte 表示符號(hào)的 ascii 碼,我們需要空間來(lái)儲(chǔ)存兩個(gè)額外信息: 一,這個(gè)符號(hào)開(kāi)始位置,一個(gè)“starter”的位置,用生物學(xué)上的話來(lái)說(shuō),就是蛋白質(zhì)翻譯時(shí)候起始密碼子AUG的位置了;二,這個(gè)符號(hào)使用的 bytes 數(shù)(其實(shí)如果每個(gè)符號(hào)都有 starter,這個(gè)長(zhǎng)度是可以不提供的,但是提供長(zhǎng)度信息增加了在部分 bytes 丟失時(shí)的容錯(cuò)能力)。解決方案是:用一個(gè) byte 的第二位是否是1來(lái)代表這一 byte 是否是一個(gè)字符的起始 byte (因?yàn)橐粋€(gè) byte 里面的第一位剛才已經(jīng)被使用了,0表示ascii碼,1表示非ascii ),即,一個(gè)多字節(jié)符號(hào)的第一 個(gè)bytes一定是 11xxxxxx,一個(gè)192到255之間的二進(jìn)制數(shù)。接下來(lái),從第三位開(kāi)始,提供長(zhǎng)度信息,第三位是0表示這個(gè)符號(hào)是2字節(jié)的,第三位開(kāi)始每多一個(gè)1,字符占用的 bytes 數(shù)加一。utf-8 最多定義到了 6 字節(jié)字符,需要比 110xxxxx 這樣的表示2字節(jié)的starter多 4 個(gè) 1,所以這個(gè)starter就是 1111110x,如上表所示。 再看看英文定義的標(biāo)準(zhǔn)吧,表達(dá)的同樣的意思:

The first byte of a multibyte sequence that represents a non-ASCII character is always in the range 0xC0 to 0xFD and it indicates how many bytes follow for this character. All further bytes in a multibyte sequence are in the range 0×80 to 0xBF. This allows easy resynchronization and makes the encoding stateless and robust against missing bytes.

真正的信息位(即,真正的charset字符集中的數(shù)字信息),是直接用二進(jìn)制的方式,依順序放在上面這個(gè)表的’x'上的。用我們中國(guó)程序員接觸最多的漢字來(lái)說(shuō)吧,它們的編碼區(qū)間是在 U-00000800 – U-0000FFFF 之間的,從上面的表中可以查到,這個(gè)區(qū)間的 utf-8 編碼是用三個(gè)字節(jié)來(lái)表示的(這就是 utf-8 編碼的漢字會(huì)比每個(gè)字符占用2 bytes的 EUC-CN 編碼的 gb2312 字符集的漢字使用更多儲(chǔ)存空間的原因),還是用 口碑的”口”字舉例吧,口字在 Unicode 中的編號(hào)是這樣的: 口: 21475 == 0×53e3 ==? 二進(jìn)制 101001111100011

在 javascript 中,run這段代碼(使用 firebug 的 console,或者編輯一個(gè)HTML將下列代碼插入一對(duì) script 標(biāo)簽之間):

alert(’\u53e3′);? //get ‘口’ alert(escape(’口’));? // get ‘%u53E3′ alert(String.fromCharCode(’21475′));? // get ‘口’ alert(’口’.charCodeAt(0));? // get ’21475‘ alert(encodeURI(’口’));? //get ‘%E5%8F%A3′

可以看到,string直接量可以用\u+十六進(jìn)制 Unicode 碼的形式得到字符 ‘口’,而fromCharCode 方法接受 10 進(jìn)制的 Unicode 碼,得到字符 ‘口’。

其中第二個(gè)alert得到的是 ‘%u7545′ , 這是一種不標(biāo)準(zhǔn)的Unicode編碼,是屬于 URI 的 Percent encoding 一部分,但這種使用方法已經(jīng)正式被 W3C 拒絕了,任何一個(gè) RFC中都沒(méi)有這個(gè)標(biāo)準(zhǔn),ECMA-262 標(biāo)準(zhǔn)中規(guī)定了 escape 的這種行為,估計(jì)也是暫時(shí)的。 比較有意思的是第五次alert得到的 ‘%E5%8F%A3′ 這是什么呢?怎么得到的呢?

這就是在URI上用的比較多的 Percent encoding,百分號(hào)編碼,RFC 3986 標(biāo)準(zhǔn)中規(guī)定的

本文來(lái)源于成都網(wǎng)站建設(shè)公司與成都網(wǎng)站設(shè)計(jì)制作公司-創(chuàng)新互聯(lián)成都公司!

分享標(biāo)題:HTML與javascript常碰到的編碼問(wèn)題
URL分享:http://www.muchs.cn/news42/311192.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)網(wǎng)站建設(shè)微信公眾號(hào)、電子商務(wù)、標(biāo)簽優(yōu)化、云服務(wù)器、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站