乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      編碼歪傳

       看見就非常 2015-04-24

      繼續(xù)上一篇。

      身為一名Web開發(fā)者,這一篇將介紹一下在Web應(yīng)用當(dāng)中常會(huì)出現(xiàn)編碼問題的地方。文中經(jīng)常會(huì)亂用“字符集”和“編碼”,不過看明白了第一篇的話相信你不會(huì)混淆概念,而且我個(gè)人覺得這兩個(gè)概念很多時(shí)候混淆也無妨……

      概念

      出于把問題描述得稍微清楚一點(diǎn)的目的,我打算先把我們的概念進(jìn)行一下定義。

      一般而言我們常遇到亂碼的場景有這樣兩種:

      1. 作為寫入端,我應(yīng)該用什么編碼來存儲(chǔ)/傳輸?
      2. 作為讀出端,我應(yīng)該用什么編碼來消費(fèi)我所收到的字節(jié)流?

      因?yàn)槲矣X得絕大多數(shù)具體場景都可以歸納成上述兩種,所以這樣應(yīng)該可以簡化一下問題。

      程序內(nèi)部處理

      現(xiàn)代編程語言一般都內(nèi)建字符串作為自帶的數(shù)據(jù)類型,一門強(qiáng)大且又實(shí)用的編程語言通常來說都有高效的字符串實(shí)現(xiàn)以及大量配套的字符串處理函數(shù)。

      在上一篇中我們有順帶提到,UTF-16因?yàn)槭且环N在處理效率和存儲(chǔ)空間之間比較平衡的,同時(shí)編碼空間又足夠大的編碼方式,在一些編程語言當(dāng)中被采用來當(dāng)作字符串的內(nèi)部編碼。比如C#、Java(可能因JVM/JDK而異)。

      一般而言編程的String類型編碼都是固定的,但是通常會(huì)提供豐富的編碼轉(zhuǎn)換函數(shù)。一種(我認(rèn)為)比較可靠的方式是:String用固定編碼方式實(shí)現(xiàn),以使得標(biāo)準(zhǔn)的字符串函數(shù)能夠只關(guān)注一種編碼,從而保證它的正確性,也能夠最大程度地針對性優(yōu)化;而通過使用類庫來將String轉(zhuǎn)換為特定編碼的字節(jié)流,或?qū)⒆止?jié)流以特定編碼轉(zhuǎn)換成String。

      反過來看,像PHP里的字符串就比較糙,它的編碼有很大問題,如果一個(gè)字符串是多字節(jié)的(通過上一篇我們了解到除了ASCII以外基本上常用的編碼都是多字節(jié)的),處理它就要用mb_xxxx系列的函數(shù)。這對編程是一種負(fù)擔(dān),因?yàn)檫@樣就意味著String類型對字符的抽象力度不足,還是得花很多精力去關(guān)注字符串的編碼。對于PHP的程序一個(gè)辦法就是在整個(gè)程序內(nèi)部統(tǒng)一編碼,同時(shí)基于此選擇好使用那一組字符串處理函數(shù)(作為項(xiàng)目規(guī)范),避免程序內(nèi)部關(guān)心編碼的問題,只把編碼暴露在與外界交換數(shù)據(jù)的地方。

      存儲(chǔ)/傳輸

      管你是什么程序,程序所生成的東西總要被消費(fèi)才有意義(不然就變成烤機(jī)程序了)。Web應(yīng)用里最常見的兩種對程序結(jié)果的消費(fèi)方式,一是把它存儲(chǔ)(數(shù)據(jù)庫、文件)起來,二是把它傳輸給用戶(瀏覽器)以供展現(xiàn)。

      當(dāng)需要存儲(chǔ)/傳輸文本的時(shí)候,就需要高度關(guān)心字符編碼了。

      存儲(chǔ)

      很多人遇到的問題是把用戶表單提交的東西寫進(jìn)MySQL里面以后亂碼了,這個(gè)問題一些可能的原因有:

      1. 提交內(nèi)容的字符編碼
      2. 服務(wù)端程序(如PHP)內(nèi)部使用的編碼
      3. MySQL傳輸時(shí)候使用的編碼
      4. MySQL數(shù)據(jù)庫聲明和使用的字符集

      第1點(diǎn)下一步會(huì)更詳細(xì)的展開。

      第2點(diǎn)在上文當(dāng)中有一定介紹了,PHP程序所接收的字節(jié)流被當(dāng)作字符串看待后,我們的程序必須要選擇合適的字符串處理函數(shù),結(jié)果才會(huì)是對的。比如一個(gè)截?cái)喑绦蛞苷_處理多字節(jié)編碼,如果把多字節(jié)編碼切斷成“半個(gè)字符”嚴(yán)重的時(shí)候甚至?xí)斐蒔HP出core。

      第3點(diǎn)就是PHP中常見的mysqli_set_charset所覆蓋的范圍,沒錯(cuò),因?yàn)镸ySQL其實(shí)是服務(wù),所以這個(gè)存儲(chǔ)其實(shí)也是傳輸。

      第4點(diǎn)就是在建庫建表的時(shí)候選的那個(gè)字符集和編碼。

      這當(dāng)中的重點(diǎn)的就是2需要對1的編碼有預(yù)期,正確的把1的字節(jié)流解析出來,轉(zhuǎn)換成程序內(nèi)部字符串實(shí)現(xiàn)所使用的編碼,套用正確的算法,接下來與MySQL驅(qū)動(dòng)和服務(wù)之間使用雙方預(yù)期的編碼,最終以數(shù)據(jù)庫定義的時(shí)候所聲明的字符集保存下來。

      傳輸

      一個(gè)HTTP請求發(fā)出的時(shí)候,用戶代理(UserAgent,通常是瀏覽器)可以通過HTTP Request Header中的Accept-Charset字段來顯式聲明預(yù)期返回的編碼,這是一種協(xié)商手段?,F(xiàn)在的瀏覽器都很流弊,啥編碼都能解析,于是直接懶得發(fā)這個(gè),言下之意就是服務(wù)端給返回什么就消化什么。

      對于服務(wù)端而言,如果收到的請求指定了Accept-Charset那么應(yīng)該按照請求者的預(yù)期來決定響應(yīng)內(nèi)容的編碼,如果沒有指定,則可以“自由發(fā)揮”,這種時(shí)候理論上說你用什么編碼都可以,但最終都必須通過某種手段告訴請求者響應(yīng)內(nèi)容是什么編碼。

      方式1:使用HTTP Response Header中Content-Type來給響應(yīng)內(nèi)容聲明編碼。比如Content-Type: text/html; charset=UTF-8。這里有個(gè)小插曲,在IE6(沒記錯(cuò)的話)里用Ajax請求的時(shí)候如果Response寫的是小寫utf-8就會(huì)跪,必須要大寫。別問我為什么知道,說起來都是淚,那是一個(gè)風(fēng)雨交加的深夜……

      方式2:通過HTML頁面頭部的<meta charset="xxx">標(biāo)簽來給頁面聲明編碼。如果Response Header里不寫編碼,瀏覽器就會(huì)嘗試找這個(gè)標(biāo)簽,然后將接下來的內(nèi)容以這個(gè)編碼解讀。這就是為什么我們提倡將<meta charset="xxx">寫在<title>標(biāo)簽之前的原因,如果<title>出現(xiàn)在此之前,它里面的字符就不知道該用什么編碼來解讀了,直觀的說就是可能造成title亂碼。

      一旦決定了編碼,服務(wù)端程序就會(huì)將字符以該種編碼最終寫入字節(jié)流,傳給客戶端。

      那如果兩種方式都用了,口徑卻不一致會(huì)怎么辦?首先當(dāng)然是給開發(fā)者賞兩耳光,然后有興趣的可以做做實(shí)驗(yàn)看看不同的瀏覽器會(huì)有什么不同的兼容策略。

      用戶提交內(nèi)容

      上面有說表單提交也有個(gè)編碼的問題,其實(shí)包括Ajax請求等,只要是客戶端向服務(wù)端發(fā)送內(nèi)容,都一樣,但通過上面的例子我想你已經(jīng)明白了,這完全是鏡像的,這次瀏覽器扮演著信息的生產(chǎn)者的角色,本質(zhì)是完全一樣的。

      消費(fèi)

      給你一本書,你怎么知道它是中文版還是英文版?“我靠,它用英文寫的就是英文版,用中文寫的就是中文版啊?!?/p>

      人類的大腦簡直聰明得要命了,這種問題根本不需要?jiǎng)幽X子,計(jì)算機(jī)就要笨多了。其實(shí)并不是計(jì)算機(jī)笨,而是這個(gè)問題在計(jì)算機(jī)的領(lǐng)域里面太難了。比如上一篇文章說到GB2312是兼容ASCII的,那么如果收到的內(nèi)容前幾個(gè)字節(jié)是3C 68 74 6D 6C 3E也就是<html>的ASCII編碼,也許臆想它是ASCII的,于是后面出現(xiàn)的雙字節(jié)字符可能就會(huì)遭殃了。UTF-8有一個(gè)很不錯(cuò)的性質(zhì)是它比較容易識別,但是也有錯(cuò)誤率和效率問題。所以這些你猜來我猜去的不靠譜的倒霉事情就只讓它出現(xiàn)在男女情愛當(dāng)中吧不要來污染我們純凈的計(jì)算機(jī)世界了好嗎。

      上面一節(jié)當(dāng)中有說到,一個(gè)靠譜的信息生產(chǎn)者,會(huì)在給你傳遞信息的時(shí)候協(xié)商或聲明編碼。身為一個(gè)合格的信息消費(fèi)者,瀏覽器可以通過這些聲明來選擇正確的編碼,解讀字節(jié)流。

      瀏覽器也是個(gè)程序,于是它內(nèi)部也會(huì)有字符串實(shí)現(xiàn),也許它用自帶字符串的語言實(shí)現(xiàn)的,也許它用自己實(shí)現(xiàn)的字符串(如C/C++),不管怎樣,有了明確的編碼,瀏覽器都能夠?qū)⑺@得的字節(jié)流轉(zhuǎn)換成自己所使用的內(nèi)部編碼。

      事已至此,似乎只要生產(chǎn)者靠譜,消費(fèi)者要注意的問題就非常少了。在服務(wù)端我們小心翼翼地處理那么多環(huán)節(jié)的編碼問題,到了瀏覽器好像已經(jīng)完事兒了。不管這之前有再多波折,瀏覽器內(nèi)部各種對字符的處理再多,基本上都不會(huì)有編碼的問題了,簡直太沒勁了,于是這里稍微發(fā)散思維一下。

      接下來瀏覽器就需要把字符顯示出來,我們考慮瀏覽器通過操作系統(tǒng)給它提供的API。API要么規(guī)定編碼要么協(xié)商/聲明編碼對吧,如果是前者,瀏覽器需要把自己內(nèi)部用的編碼轉(zhuǎn)換成API所預(yù)期的編碼,然后調(diào)用API——在這個(gè)場景里面,瀏覽器又從信息的消費(fèi)者變成生產(chǎn)者了對吧,而這次操作系統(tǒng)是消費(fèi)者。

      然后我們假設(shè)操作系統(tǒng)將會(huì)用某種字體渲染這段字符,字體文件內(nèi)部一般都對每個(gè)字符進(jìn)行編號,現(xiàn)代的字體一般都會(huì)用Unicode,沒錯(cuò),我們又回到了字符集的概念。操作系統(tǒng)將字符編碼還原到字符集當(dāng)中的字符編號(顯然對于變長字節(jié)編碼這個(gè)過程要一些運(yùn)算),在字體文件內(nèi)通過編號查到這個(gè)字符,一個(gè)設(shè)計(jì)良好的字體可能對同一個(gè)字符會(huì)設(shè)計(jì)了多個(gè)字形(Glyph),比如Regular體一個(gè)、粗體一個(gè)、斜體一個(gè),甚至還有更多更多,比如組合字符、一些特殊規(guī)則下的變形字符,不展開討論。

      這些渲染規(guī)格都是在API里指定好的,然后就用對應(yīng)的字形來進(jìn)行渲染。渲染字形這事兒還不是一個(gè)簡單的事情,字體分點(diǎn)陣的、矢量的(甚至圖片的?),不同的渲染引擎,例如Windows上的GDI、DirectWrite、第三方的GDI++、MacType,還有OSX的渲染引擎,Linux不同的桌面系統(tǒng)的渲染引擎,在最終把字形繪制成像素點(diǎn)的算法上有細(xì)節(jié)區(qū)別。

      上面說的還只是渲染單個(gè)字符的時(shí)候的問題,在此之前還要做文字的排版啊什么的,哪怕看起來很小一件事情也夠人鉆大半輩子了。我的天,人類為了在計(jì)算機(jī)上展示文字到底下了多少功夫?

      好的好的,剛才似乎發(fā)散的太多了,就此打住,總之就瀏覽器而言對于一個(gè)HTML頁面的消費(fèi)差不多是可以理解了。

      階段性小結(jié)

      把亂碼的問題從一個(gè)信息的生產(chǎn)者和消費(fèi)者兩個(gè)角度來看,中間所經(jīng)歷的哪些環(huán)節(jié)涉及到編碼,哪些環(huán)節(jié)涉及到編碼的協(xié)商與聲明,就明確多了。上面的例子其實(shí)很容易就可以舉一反三。

      于是一些常見的諸如“PHPMyAdmin里看是正常的,頁面上是亂碼”或者“頁面上是正常的,PHPMyAdmin里看著是亂碼”這種問題可能會(huì)是哪些個(gè)環(huán)節(jié)闖的禍心里就已經(jīng)有譜了。對于各種接口,比如與MySQL通訊,比如與后端之間的接口,如何協(xié)商/聲明編碼,什么時(shí)候需要轉(zhuǎn)換編碼,心里面也有譜了。

      預(yù)報(bào)

      呵呵呵呵,這次的內(nèi)容雖然沒那么理論,但是還是太簡單了嘛,看到亂碼就查編碼唄你當(dāng)我是傻X呢。

      這時(shí)候也有觀眾吐槽:“那么各種程序當(dāng)中用的編碼比如URL Encode、Base64又是些啥玩意啊老濕?”

      也有好奇心過盛的觀眾要問:“問號和方塊是怎么回事?屯屯屯燙燙燙錕斤拷又是些什么鬼呢老濕?”

      對于上面的問題我只想說四個(gè)字:請聯(lián)系我請看終篇:《編碼歪傳——番外篇》

        本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多