檔案編碼 Web篇

2021-07-29 14:47:14 字數 3787 閱讀 1319

身為一名web開發者,這一篇將介紹一下在web應用當中常會出現編碼問題的地方。文中經常會亂用「字符集」和「編碼」,不過看明白了第一篇的話相信你不會混淆概念,而且我個人覺得這兩個概念很多時候混淆也無妨……

出於把問題描述得稍微清楚一點的目的,我打算先把我們的概念進行一下定義。

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

作為寫入端,我應該用什麼編碼來儲存/傳輸

作為讀出端,我應該用什麼編碼來消費我所收到的位元組流?

因為我覺得絕大多數具體場景都可以歸納成上述兩種,所以這樣應該可以簡化一下問題。

現代程式語言一般都內建字串作為自帶的資料型別,一門強大且又實用的程式語言通常來說都有高效的字串實現以及大量配套的字串處理函式。

一般而言程式設計的string型別編碼都是固定的,但是通常會提供豐富的編碼轉換函式。一種(我認為)比較可靠的方式是:string用固定編碼方式實現,以使得標準的字串函式能夠只關注一種編碼,從而保證它的正確性,也能夠最大程度地針對性優化;而通過使用類庫來將string轉換為特定編碼的位元組流,或將位元組流以特定編碼轉換成string。

管你是什麼程式,程式所生成的東西總要被消費才有意義(不然就變成烤機程式了)。web應用裡最常見的兩種對程式結果的消費方式,一是把它儲存(資料庫、檔案)起來,二是把它傳輸給使用者(瀏覽器)以供展現。

當需要儲存/傳輸文字的時候,就需要高度關心字元編碼了。

很多人遇到的問題是把使用者表單提交的東西寫進mysql裡面以後亂碼了,這個問題一些可能的原因有:

提交內容的字元編碼

服務端程式(如php)內部使用的編碼

mysql傳輸時候使用的編碼

mysql資料庫宣告和使用的字符集

第1點下一步會更詳細的展開。

第2點在上文當中有一定介紹了,php程式所接收的位元組流被當作字串看待後,我們的程式必須要選擇合適的字串處理函式,結果才會是對的。比如乙個截斷程式要能正確處理多位元組編碼,如果把多位元組編碼切斷成「半個字元」嚴重的時候甚至會造成php出core。

第3點就是php中常見的mysqli_set_charset所覆蓋的範圍,沒錯,因為mysql其實是服務,所以這個儲存其實也是傳輸。

第4點就是在建庫建表的時候選的那個字符集和編碼。

這當中的重點的就是2需要對1的編碼有預期,正確的把1的位元組流解析出來,轉換成程式內部字串實現所使用的編碼,套用正確的演算法,接下來與mysql驅動和服務之間使用雙方預期的編碼,最終以資料庫定義的時候所宣告的字符集儲存下來。

乙個http請求發出的時候,使用者**(useragent,通常是瀏覽器)可以通過http request header中的accept-charset欄位來顯式宣告預期返回的編碼,這是一種協商手段。現在的瀏覽器都很流弊,啥編碼都能解析,於是直接懶得發這個,言下之意就是服務端給返回什麼就消化什麼。

對於服務端而言,如果收到的請求指定了accept-charset那麼應該按照請求者的預期來決定響應內容的編碼,如果沒有指定,則可以「自由發揮」,這種時候理論上說你用什麼編碼都可以,但最終都必須通過某種手段告訴請求者響應內容是什麼編碼。

方式1:使用http response header中content-type來給響應內容宣告編碼。比如content-type: text/html; charset=utf-8。這裡有個小插曲,在ie6(沒記錯的話)裡用ajax請求的時候如果response寫的是小寫utf-8就會跪,必須要大寫。別問我為什麼知道,說起來都是淚,那是乙個風雨交加的深夜……

方式2:通過html頁面頭部的標籤來給頁面宣告編碼。如果response header裡不寫編碼,瀏覽器就會嘗試找這個標籤,然後將接下來的內容以這個編碼解讀。這就是為什麼我們提倡將寫在標籤之前的原因,如果出現在此之前,它裡面的字元就不知道該用什麼編碼來解讀了,直觀的說就是可能造成title亂碼。

一旦決定了編碼,服務端程式就會將字元以該種編碼最終寫入位元組流,傳給客戶端。

那如果兩種方式都用了,口徑卻不一致會怎麼辦?首先當然是給開發者賞兩耳光,然後有興趣的可以做做實驗看看不同的瀏覽器會有什麼不同的相容策略。

上面有說表單提交也有個編碼的問題,其實包括ajax請求等,只要是客戶端向服務端傳送內容,都一樣,但通過上面的例子我想你已經明白了,這完全是映象的,這次瀏覽器扮演著資訊的生產者的角色,本質是完全一樣的。

給你一本書,你怎麼知道它是中文版還是英文版?「我靠,它用英文寫的就是英文版,用中文寫的就是中文版啊。」

上面一節當中有說到,乙個靠譜的資訊生產者,會在給你傳遞資訊的時候協商或宣告編碼。身為乙個合格的資訊消費者,瀏覽器可以通過這些宣告來選擇正確的編碼,解讀位元組流。

瀏覽器也是個程式,於是它內部也會有字串實現,也許它用自帶字串的語言實現的,也許它用自己實現的字串(如c/c++),不管怎樣,有了明確的編碼,瀏覽器都能夠將所獲得的位元組流轉換成自己所使用的內部編碼。

事已至此,似乎只要生產者靠譜,消費者要注意的問題就非常少了。在服務端我們小心翼翼地處理那麼多環節的編碼問題,到了瀏覽器好像已經完事兒了。不管這之前有再多波折,瀏覽器內部各種對字元的處理再多,基本上都不會有編碼的問題了,簡直太沒勁了,於是這裡稍微發散思維一下。

接下來瀏覽器就需要把字元顯示出來,我們考慮瀏覽器通過作業系統給它提供的api。api要麼規定編碼要麼協商/宣告編碼對吧,如果是前者,瀏覽器需要把自己內部用的編碼轉換成api所預期的編碼,然後呼叫api——在這個場景裡面,瀏覽器又從資訊的消費者變成生產者了對吧,而這次作業系統是消費者。

然後我們假設作業系統將會用某種字型渲染這段字元,字型檔案內部一般都對每個字元進行編號,現代的字型一般都會用unicode,沒錯,我們又回到了字符集的概念。作業系統將字元編碼還原到字符集當中的字元編號(顯然對於變長位元組編碼這個過程要一些運算),在字型檔案內通過編號查到這個字元,乙個設計良好的字型可能對同乙個字元會設計了多個字形(glyph),比如regular體乙個、粗體乙個、斜體乙個,甚至還有更多更多,比如組合字元、一些特殊規則下的變形字元,不展開討論。

這些渲染規格都是在api裡指定好的,然後就用對應的字形來進行渲染。渲染字形這事兒還不是乙個簡單的事情,字型分點陣的、向量的(甚至的?),不同的渲染引擎,例如windows上的gdi、directwrite、第三方的gdi++、mactype,還有osx的渲染引擎,linux不同的桌面系統的渲染引擎,在最終把字形繪製成畫素點的演算法上有細節區別。

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

好的好的,剛才似乎發散的太多了,就此打住,總之就瀏覽器而言對於乙個html頁面的消費差不多是可以理解了。

把亂碼的問題從乙個資訊的生產者和消費者兩個角度來看,中間所經歷的哪些環節涉及到編碼,哪些環節涉及到編碼的協商與宣告,就明確多了。上面的例子其實很容易就可以舉一反三。

於是一些常見的諸如「phpmyadmin裡看是正常的,頁面上是亂碼」或者「頁面上是正常的,phpmyadmin裡看著是亂碼」這種問題可能會是哪些個環節闖的禍心裡就已經有譜了。對於各種介面,比如與mysql通訊,比如與後端之間的介面,如何協商/宣告編碼,什麼時候需要轉換編碼,心裡面也有譜了。

呵呵呵呵,這次的內容雖然沒那麼理論,但是還是太簡單了嘛,看到亂碼就查編碼唄你當我是傻x呢。

這時候也有觀眾吐槽:「那麼各種程式當中用的編碼比如url encode、base64又是些啥玩意啊老濕?」

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

web編碼問題

1.js的編碼跟php的編碼不同,js解析php的編碼時候有可能會出錯的,所以建議php不要編碼返回東西給js。2.js編碼完的東西,php會自動解析,不用使用專門的解碼。如果使用瞭解碼,反而結果是錯的。php傳遞資料給js的時候,一般都是http請求的返回資料,這樣的資料不會有其他的程式進行中間處...

個人編碼修養養成篇 間隔編碼必學篇

3 區分段落 4 區分各種控制語句 5 區分各函式 6 運算子前後需要留出空格 7 不要在一元運算子與運算元之間插入空格yiyuan 8 分號前不要加空格 9 不要濫用tap鍵 10 逗號後必須插入乙個空格 11不要用很多空格來表示強調 12 變數初始化時的列對齊 13 一行只宣告乙個變數 14 小...

web前端編碼規範

web前端編碼規範 本文摘至bootstrap中文網中bootstrap編碼規範中常用部分,詳細請檢視bootstrap編碼規範 1.html 2.css 相關的屬性宣告應當歸為一組,並按照下面的順序排列 positioning box model typographic visual 單行規則宣告...