摘要:受到這個的影響,中的字符操作函數某些情況無法返回正確的結果。的碼點,還有另外一種表示方法,稱為進制轉義序列。這與我們的認知有點不同,我們通常認為一個表情符號也是一個字符,長度為。而如果通過來判斷字符串長度顯然是不夠準確的。
大家對上一篇文章中提到的UCS編碼可能比較陌生。殊不知這就是JavaScript采用的編碼方法。
既然Unicode已經統一了天下,為什么JavaScript不采用UTF的編碼方法呢?原因很簡單,因為JavaScript誕生的時候UTF-8還尚未成熟,UTF-16更是到后面才出現,而此時UCS已經先行一步地完成了UCS-2。所以JavaScript采用了比UTF更早的UCS。也就是UCS-2。(記住只是編碼方法,實際上字符集還是Unicode字符集)
UCS-2 與 UTF-16從命名上看,我們很容易猜出UCS-2占用2個字節。而UTF-16占用16位,也是2個字節,那他們的編碼方式有什么不同呢?
對于2個字節的碼點,UCS-2和UTF-16是沒有什么區別的。在基本平面上(2^16),UTF-16沿用了UCS-2的編碼,另外在輔助平面上,UTF-16還定義了4個字節的表示方法。簡單來說,UTF-16可看成是UCS-2的父集。在沒有輔助平面字符前,UTF-16與UCS-2所指的是同一的意思。但當引入輔助平面字符后,就稱為UTF-16了。
由于JavaScript只能處理UCS-2編碼,造成所有字符都是2個字節,如果是4個字節的字符,會被當做兩個雙字節的字符處理。受到這個的影響,JavaScript中的字符操作函數某些情況無法返回正確的結果。
對于兩個字節的字符,js能夠根據碼點直接輸出對應字符。例如小寫字母"a"的Unicode編碼就是U+0061。
U+0000 - U+00FF的碼點,還有另外一種表示方法,稱為16進制轉義序列。用"x"開頭,后面跟兩位的16進制符。
大于兩個字符的碼點,JavaScript就有點力不從心了。例如字符
這個符號的字符碼點為 "U+1F4A9", 控制臺的輸出結果是這樣的
這顯然不是正確的結果,那么這個符號是怎么產生的呢?
由于UCS-2每次只能讀取兩個字節,所以 "U+1F4A9"被解讀為U+1F4A 和 9, 查閱Unicode映射表U+1F4A 對應的是希臘語的擴展,就是是符號0加一點。
剩下的9則被識別為普通的字符串符號"9"輸出了。
既然JavaScript無法處理大于兩個字節的符號,那對于互聯網上成千上萬的復雜字符和表情,豈不是束手無策?
非也!
我們在控制臺輸出這個碼點:”uD83DuDCA9″
神奇的事情發生了,”uD83DuDCA9″竟然也能輸出符號。
如果我們多帶帶輸出這兩個碼點,看會輸出什么字符:
兩個字符多帶帶輸出都是亂碼,Unicode無法識別對應的字符。再次查閱映射表。
發現這兩個碼點分別落在了UTF-16的高半位和低半位。
原來UTF-16碰到第一個雙字節碼點在D800-DBFF之間時,代碼不會直接讀取符號,而是將其存儲為高半區,再往下讀取兩個字節的低半區,合在一起再輸出符號。而這也是UCS-2的處理方式。
那么 "U+1F4A9"怎么轉化為高低位”uD83DuDCA9"呢,下面是轉換公式:
H = Math.floor((0x1F4A9-0x10000)/0x400)+0xD800 = 0xD83D L = (0x1F4A9-0x10000) % 0x400+0xDC00 = 0xDCA9
既然我們已經能夠在JavaScript中輸出輔助平面的字符了,那不是萬事大吉了嗎?
常見問題考慮一個常用的前端場景——輸入框,通常會規定最大輸入字數。嘗試輸出上面的符號長度, 發現長度是2。
這與我們的認知有點不同,我們通常認為一個表情符號也是一個字符,長度為1。而如果通過"xxx".length 來判斷字符串長度顯然是不夠準確的。這個問題在ES6中能迎刃而解:
ES6中通過Array.from能準確讀取字符長度
然而Array.from不是完美的,在某些場景下也無法滿足需求,況且還存在ES6的瀏覽器兼容性問題。
在ES5中,我們通過正則的判斷,也能得到Array.from的效果,而且擴展性更高:
var regexAstralSymbols = /[uD800-uDBFF][uDC00-uDFFF]/g; function countSymbols(string) { return string // 替換掉輔助平面的連字符 .replace(regexAstralSymbols, "_") .length; } countSymbols("uD835uDC00"); //1
另外,JavaScript也提供了從碼點到字符的轉換函數。
//這里直接輸入進制數0x0061或97,而不是字符串 String.fromCharCode(0x0061); //a //輸出為10進制數 "a".charCodeAt(0);//97 (16進制0x0061)
而對于附加平面的符號,JavaScript又要跪了, 直接輸出低位 U+F4A9的字符,而該字符位于Unicode的私用區,未定義,所以輸出"?"。
String.fromCharCode(0x1F4A9);//"?"
同樣的,我們將符號U+1F4A9變為高地位輸入,就能成功輸出符號
對于fromCharCode和charCodeAr這兩個方法,ES6 也提供了新的接口,對應fromCodePoint和codePointAt,問題得到解決:
在處理字符串逆轉,正則的匹配上,附加字符都會有問題,要處理這些問題,只有一條準則,就是要對附加碼點做特殊處理。在ES6還沒全面支持的情況下,只能通過定義各種hack方法來解決。
關于Unicode跟JavaScript的糾葛就講到這,亂碼問題讓人費解,但是只要了解了基本原理,問題往往就能迎刃而解。
參考文章:
https://zh.wikipedia.org/wiki
https://mathiasbynens.be/note...
http://www.ruanyifeng.com/blo...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86749.html
摘要:所以中國人自己創造了一種字符編碼,每個漢字和符號用兩個字節來表示。第一個字節稱為高位字節,第二個字節稱為低位字節。而目前為止我們使用最廣泛的中文編碼還是。 網站開發中經常會被亂碼問題困擾。知道文件編碼錯誤會導致亂碼,但對其中的原理卻知之甚少。偶然從某篇文章了解了Unicode,發現從這條線出發也牽引出了一系列缺失的知識點。通過研讀文章,基本了解了一些以前不明白的問題,所以整理了幾篇,從...
摘要:在各種論壇上,經常會看到一些奇怪的字符,它們的內容會超出顯示范圍,舉個例子常見的還有一些有泰文字符組成的。第一種是對字符串文字區域設置最大高度,超出的部分自動隱藏。將附加字符進行過濾,這種方法在某種程度上會誤殺一些需要正常顯示的附加符號。 在各種論壇上,經常會看到一些奇怪的字符,它們的內容會超出顯示范圍, 舉個例子: Z??????????????????A????????L?????...
摘要:我們首先了解一下中有關類型轉換的知識。新增類型拋出異常從列表可以明顯看到少了一個類型轉換為的規則。這里要強調一點第二個表達式沒有涉及到強制類型轉換。如果文中有錯誤或者有某些強制轉換的情形沒有涉及到請及時留言告知,我會修改并補充進去。 javascript是一門非常奇特的語言,它有時候奇特的會讓人懷疑人生。比如讓我們看一下下面的一些奇葩例子: false == 0 ...
摘要:本文大部分內容轉自阮一峰前輩的文章,更新了部分內容并加入了部分自己的理解。字符串處理函數新增了幾個專門處理字節碼點的函數。參考鏈接阮一峰與詳解輔助平面入門 本文大部分內容轉自 阮一峰前輩的文章,更新了部分內容并加入了部分自己的理解。 Unicode是什么? Unicode源于一個很簡單的想法:將全世界所有的字符包含在一個集合里,計算機只要支持這一個字符集,就能顯示所有的字符,再也不會有...
閱讀 1176·2021-11-23 10:10
閱讀 1499·2021-09-30 09:47
閱讀 887·2021-09-27 14:02
閱讀 2967·2019-08-30 15:45
閱讀 3020·2019-08-30 14:11
閱讀 3610·2019-08-29 14:05
閱讀 1820·2019-08-29 13:51
閱讀 2206·2019-08-29 11:33