瀏覽器根據charset判斷編碼方式的疑問?
伺服器返回html對應的二進位數據,前端獲得後,根據meta中的charset來把二進位數據解碼成對應的文本,然後根據content-type來設置呈現的形式,比如text/html和text/plain就有本質的不同。
我很好奇的一點是,瀏覽器是根據charset判斷編碼方式,而charset裡面的欄位又是經過二進位解碼而來的,變成了雞生蛋的問題?
可能是我哪裡理解錯了,求解答..
Content-Type是HTTP Header裡面的東西,和meta不能相提並論。瀏覽器首先要確定這是一個HTML文件才會嘗試解析meta charset設置,所以如果Content-Type不對根本沒有meta什麼事兒。
通常來說,瀏覽器會先按照Content-Type的編碼設置來解析文本,然後在解析過程中發現charset設置,再更換編碼重新讀取。若Content-Type沒有設置編碼,或者說這個HTML文件根本就不是走的HTTP協議,瀏覽器通常會猜測編碼來解析文本,然後發現charset設置再更換編碼讀取。
所以通常來說,如果想要在meta中設置charset,那麼這個編碼方式至少需要是ASCII兼容的,否則瀏覽器的確有可能無法處理。
最好的方式是直接在Content-Type中給出編碼,這樣大家都省事。&中指定的編碼應當只作為獨立HTML文件(非通過HTTP協議獲取)解析讀取時才用到。
你並沒有理解錯,這確實有雞生蛋的問題,因此在開始的時候是需要先猜測文檔的字元編碼的。
先說 content-type,現代瀏覽器都是根據伺服器發送的 Content-Type http頭來的,少數情況會考慮其他因素(如由script/style/link標籤發起的請求)。以前也有基於內容嗅探的,但存在安全隱患,因此現在都不用了。
然後說字元編碼,首先 Content-Type 頭裡可以指定字元編碼,如果沒有指定,那麼就要根據內容來猜,比如如果有 Byte order mark - Wikipedia 的話,就可以確定編碼方式。
如果沒有 BOM 的情況,就按照 UTF-8 的方式解析,絕大部分的 legacy 編碼,比如 iso-8859-x、GBK、Big-5、Shift-JIS 等,在 0~127 的範圍內是和 UTF-8 兼容的,而 html 起始的部分,通常也都在這個範圍內。等解析到 charset 部分之後,可以再切換 encoding。
也因此,如果你沒有在 Content-Type 頭裡指定 encoding,需要依賴 & 來指定,就應該確保在 charset 之前沒有用 0~127 範圍以外的字元,通常來說,你只要確保 & 寫在盡量前面(比如在可能包含特定語言字元的 &
最後,新的Web標準要求盡量使用 UTF-8,所以如果不是 legacy 項目,就不要再用任何 legacy 的 character encoding 了。
瀏覽器實現細節各有差異,但是對charset的處理基本上是這樣的過程。
首先,在HTTP報文的header中,如果Content-Type包含了charset,那最好,瀏覽器就按照這個charset來解析整個HTML,比如包含下面的header。
Content-Type: text/html; charset=UTF-8
那麼就按照UTF-8來解析HTML內容。
如果HTTP報文的header中不包含Content-Type,或者Content-Type里沒有包含charset,那瀏覽器就要多費點勁了。因為幾乎所有字元編碼方式都和ASCII兼容,也就是說對於26個英文字母等字元的編碼是一樣的,所以瀏覽器就用ASCII的編碼方式來解析HTML,希望從HTML的HEAD部分中找到線索,比如在HTML中發現了下面的內容。
&
&
瀏覽器就知道原來網頁應該是UTF-8編碼,這時候它就會立刻中止按照ASCII編碼來解析網頁的過程,重新按照UTF-8編碼來解析。這過程當然會有一些浪費,所以我們應該盡量在HTTP的Header部分指定charset。
如果HTML中也沒有指定編碼方式,那瀏覽器就只好靠猜了,當然有可能猜錯,於是出現頁面亂碼。
所謂先有雞還是先有蛋的問題,因為雞和蛋都可以吃,吃一口就試出來了:-)同樣,對於charset,因為所有charset都和ASCII兼容,所以也可以試出來。
以前看chrome源碼分析過編碼問題,不過現在記得不多了。題主可以去看看源碼。
前面答案說了會判斷http header的中的charset,也會提取HTML頁面的charset。這裡有一個優先順序的問題,但是我忘了是哪個的優先順序更高。
優先順序最高的是BOM,有BOM的就按BOM來解碼。
我記得提取HTML中的charset是把HTML前面的一部分位元組當作ASCII來解析出charset,不確定記錯了沒有。
要是沒有BOM,也沒有charset信息,會有一個默認編碼,現在應該會去自動detect編碼。
補充,源碼主要是這三個文件:
https://chromium.googlesource.com/chromium/blink/+/master/Source/core/loader/TextResourceDecoderBuilder.cpp
https://chromium.googlesource.com/chromium/blink/+/master/Source/core/html/parser/TextResourceDecoder.cpp
https://chromium.googlesource.com/chromium/blink/+/master/Source/core/html/parser/HTMLMetaCharsetParser.cpp
和我上面說的差不多,就不仔細分析了。
首先是BOM,沒有BOM就看http header的,在沒有就看html中的meta的。
還有設置了autodetect就是autodetect,不過我沒看後面的,不知道autodetect的時候會不會覆蓋前面有的encoding。
解析html meta中的charset是當做asciii來的,這點沒記錯。
題主看的時候注意有個EncodingSource,這個標記了編碼信息的來源,有助於理解整個流程。
瀉藥
看這個就好了
https://www.w3.org/International/questions/qa-html-encoding-declarations
基本說了這麼幾個事兒:
最直接的方式為通過 HTTP 協議([RFC2616], 3.4 及 14.17) "Content-Type" 頭欄位的 "charset" 將文檔的字元編碼告訴用戶端。
沒有的話就是 HTML 的前 1024 位元組嗅探,找 meta charset http-equiv content來確定編碼類型
一些瀏覽器還會根據 BOM頭來做輔助確定
html返回的是文本,不是二進位。文本有不同編碼。
charset存在於http header部分,而RFC強制規定http header部分的編碼是八比特ASCII編碼,所以charset欄位不存在編碼不確定的情況.詳情可參閱相關的RFC文檔.
推薦閱讀:
※學習 HTML+CSS 的經典著作有哪些?
※女孩子做前端開發容易嗎?最多能做幾年?
※應屆生如何在四個月後找到一份web前端的工作?
※大公司面試前端開發類的職位時,更看重的是什麼?