1. 字元發展歷史
1.1 位元組
- 計算機內部,所有資訊最終都是一個二進位制值
- 每一個二進位制位(bit)有0和1兩種狀態,因此八個二進位制位就可以組合出256種狀態,這被稱為一個位元組(byte)
1.2 單位
- 8位 = 1位元組
- 1024位元組 = 1K
- 1024K = 1M
- 1024M = 1G
- 1024G = 1T
1.3 JavaScript中的進位制
1.3.1 進製表示
let a = 0b10100;//二進位制
let b = 0o24;//八進位制
let c = 20;//十進位制
let d = 0x14;//十六進位制
console.log(a == b);
console.log(b == c);
console.log(c == d);
複製程式碼
1.3.2 進位制轉換
- 10進位制轉任意進位制 10進位制數.toString(目標進位制)
console.log(c.toString(2)); 複製程式碼
- 任意進位制轉十進位制 parseInt('任意進位制字串', 原始進位制);
console.log(parseInt('10100', 2)); 複製程式碼
1.4 ASCII
最開始計算機只在美國用,八位的位元組可以組合出256種不同狀態。0-32種狀態規定了特殊用途,一旦終端、印表機遇上約定好的這些位元組被傳過來時,就要做一些約定的動作,如:
- 遇上0×10, 終端就換行;
- 遇上0×07, 終端就向人們嘟嘟叫;
又把所有的空格、標點符號、數字、大小寫字母分別用連續的位元組狀態表示,一直編到了第 127 號,這樣計算機就可以用不同位元組來儲存英語的文字了
這128個符號(包括32個不能列印出來的控制符號),只佔用了一個位元組的後面7位,最前面的一位統一規定為0
這個方案叫做 ASCII 編碼
American Standard Code for Information Interchange:美國資訊互換標準程式碼
1.5 GB2312
後來西歐一些國家用的不是英文,它們的字母在ASCII裡沒有為了可以儲存他們的文字,他們使用127號這後的空位來儲存新的字母,一直編到了最後一位255。比如法語中的é的編碼為130。當然了不同國家表示的符號也不一樣,比如,130在法語編碼中代表了é,在希伯來語編碼中卻代表了字母Gimel (ג)。
從128 到 255 這一頁的字符集被稱為擴充套件字符集。
中國為了表示漢字,把127號之後的符號取消了,規定
- 一個小於127的字元的意義與原來相同,但兩個大於 127 的字元連在一起時,就表示一個漢字;
- 前面的一個位元組(他稱之為高位元組)從
0xA1
用到0xF7
,後面一個位元組(低位元組)從0xA1
到0xFE
; - 這樣我們就可以組合出大約7000多個(247-161)*(254-161)=(7998)簡體漢字了。
- 還把數學符號、日文假名和ASCII裡原來就有的數字、標點和字母都重新編成兩個字長的編碼。這就是全形字元,127以下那些就叫半形字元。
- 把這種漢字方案叫做 GB2312。GB2312 是對 ASCII 的中文擴充套件
1.6 GBK
後來還是不夠用,於是乾脆不再要求低位元組一定是 127 號之後的內碼,只要第一個位元組是大於 127 就固定表示這是一個漢字的開始,又增加了近 20000 個新的漢字(包括繁體字)和符號。
1.7 GB18030 / DBCS
又加了幾千個新的少數民族的字,GBK
擴成了GB18030
通稱他們叫做 DBCS
Double Byte Character Set:雙位元組字符集。
在 DBCS 系列標準裡,最大的特點是兩位元組長的漢字字元和一位元組長的英文字元並存於同一套編碼方案裡
各個國家都像中國這樣搞出一套自己的編碼標準,結果互相之間誰也不懂誰的編碼,誰也不支援別人的編碼
1.8 Unicode
ISO 的國際組織廢了所有的地區性編碼方案,重新搞一個包括了地球上所有文化、所有字母和符 的編碼! Unicode 當然是一個很大的集合,現在的規模可以容納100多萬個符號。
- International Organization for Standardization:國際標準化組織。
- Universal Multiple-Octet Coded Character Set,簡稱 UCS,俗稱 Unicode
ISO 就直接規定必須用兩個位元組,也就是 16 位來統一表示所有的字元,對於 ASCII 裡的那些 半形字元,Unicode 保持其原編碼不變,只是將其長度由原來的 8 位擴充套件為16 位,而其他文化和語言的字元則全部重新統一編碼。
從 Unicode 開始,無論是半形的英文字母,還是全形的漢字,它們都是統一的一個字元!同時,也都是統一的 兩個位元組
- 位元組是一個8位的物理存貯單元,
- 而字元則是一個文化相關的符號。
1.9 UTF-8
Unicode 在很長一段時間內無法推廣,直到網際網路的出現,為解決 Unicode 如何在網路上傳輸的問題,於是面向傳輸的眾多 UTF 標準出現了,
Universal Character Set(UCS)Transfer Format:UTF編碼
- UTF-8 就是在網際網路上使用最廣的一種 Unicode 的實現方式
- UTF-8就是每次以8個位為單位傳輸資料
- 而UTF-16就是每次 16 個位
- UTF-8 最大的一個特點,就是它是一種變長的編碼方式
- Unicode 一箇中文字元佔 2 個位元組,而 UTF-8 一箇中文字元佔 3 個位元組
- UTF-8 是 Unicode 的實現方式之一
1.10 編碼規則
- 對於單位元組的符號,位元組的第一位設為0,後面7位為這個符號的 Unicode 碼。因此對於英語字母,UTF-8 編碼和 ASCII 碼是相同的。
- 對於n位元組的符號(n > 1),第一個位元組的前n位都設為1,第n+ 1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位制位,全部為這個符號的 Unicode 碼。
Unicode符號範圍 | UTF-8編碼方式
(十六進位制) | (二進位制)
----------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
複製程式碼
function transfer(num) {
let ary = ['1110', '10', '10'];
let binary = num.toString(2);
ary[2] = ary[2]+binary.slice(binary.length-6);
ary[1] = ary[1]+binary.slice(binary.length-12,binary.length-6);
ary[0] = ary[0]+binary.slice(0,binary.length-12).padStart(4,'0');
let result = ary.join('');
return parseInt(result,2).toString(16);
}
//萬
let result = transfer(0x4E07);//E4B887
複製程式碼
1.11 聯通不如移動
C1 1100 0001
AA 1010 1010
CD 1100 1101
A8 1010 1000
0000000001101010->006A(106)->j
0000001101101000->0368(872)->?
複製程式碼
1.12 文字編碼
使用NodeJS編寫前端工具時,操作得最多的是文字檔案,因此也就涉及到了檔案編碼的處理問題。我們常用的文字編碼有UTF8和GBK兩種,並且UTF8檔案還可能帶有BOM。在讀取不同編碼的文字檔案時,需要將檔案內容轉換為JS使用的UTF8編碼字串後才能正常處理。
1.12.1 BOM的移除
BOM用於標記一個文字檔案使用Unicode編碼,其本身是一個Unicode字元("\uFEFF"),位於文字檔案頭部。在不同的Unicode編碼下,BOM字元對應的二進位制位元組如下:
Bytes Encoding
----------------------------
FE FF UTF16BE
FF FE UTF16LE
EF BB BF UTF8
複製程式碼
因此,我們可以根據文字檔案頭幾個位元組等於啥來判斷檔案是否包含BOM,以及使用哪種Unicode編碼。但是,BOM字元雖然起到了標記檔案編碼的作用,其本身卻不屬於檔案內容的一部分,如果讀取文字檔案時不去掉BOM,在某些使用場景下就會有問題。例如我們把幾個JS檔案合併成一個檔案後,如果檔案中間含有BOM字元,就會導致瀏覽器JS語法錯誤。因此,使用NodeJS讀取文字檔案時,一般需要去掉BOM
function readText(pathname) {
var bin = fs.readFileSync(pathname);
if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) {
bin = bin.slice(3);
}
return bin.toString('utf-8');
}
複製程式碼
1.12.2 GBK轉UTF8
NodeJS支援在讀取文字檔案時,或者在Buffer轉換為字串時指定文字編碼,但遺憾的是,GBK編碼不在NodeJS自身支援範圍內。因此,一般我們藉助iconv-lite這個三方包來轉換編碼。使用NPM下載該包後,我們可以按下邊方式編寫一個讀取GBK文字檔案的函式。
var iconv = require('iconv-lite');
function readGBKText(pathname) {
var bin = fs.readFileSync(pathname);
return iconv.decode(bin, 'gbk');
}複製程式碼