首頁  >  文章  >  web前端  >  聊聊Node.js Buffer中的encoding(程式設計)

聊聊Node.js Buffer中的encoding(程式設計)

青灯夜游
青灯夜游轉載
2021-08-31 10:28:233401瀏覽

這篇文章帶大家了解一下Node.js Buffer中的encoding,希望對大家有幫助!

聊聊Node.js Buffer中的encoding(程式設計)

電腦最小的單位是一個位,也就是 0 和 1,在硬體上透過高低電平來對應。但只有一位表示的資訊太少了,所以又規定了 8 個位元為一個字節,之後數字、字串等各種資訊都是基於位元組來儲存的。 【推薦學習:《nodejs 教學》】

字元怎麼儲存呢?就是靠編碼,不同的字元對應不同的編碼,然後在需要渲染的時候依照對應編碼去查字體庫,然後渲染對應字元的圖形。

字符集

字符集(charset)最早是 ASCII 碼,也就是 abc ABC 123 等 128 個字符,因為電腦最早就是美國發明的。後來歐洲也制定了一套字符集標準,叫做 ISO,後來中國也搞了一套,叫做 GBK。

國際標準化組織覺得不能這樣各自搞一套,不然同一個編碼在不同字符集裡面就不同的意思,於是就提出了unicode 編碼,把全世界大部分編碼收錄,這樣每個字符只有唯一的編碼。

但是ASCII 碼只需要1 個字節就可以存儲,而GBK 需要2 個字節,還有的字符集需要3 個字節等,有的只要一個字節存儲卻存了2個字節,比較浪費空間。所以就出現了 utf-8、utf-16、utf-24 等不同編碼方案。

utf-8、utf-16、utf-24 都是 unicode 編碼,但具體實作方案不同。

UTF-8 為了節省空間,設計了從 1 到 6 個位元組的變長儲存方案。而 UTF-16 是固定 2 個位元組,UTF-24 是固定 4 個位元組。

聊聊Node.js Buffer中的encoding(程式設計)

最後,UTF-8 因為佔用空間最少,所以被廣泛地應用。

Node.js 的 Buffer 的 encoding

每種語言都支援字元集的編碼解碼,Node.js 也一樣。

Node.js 裡面可以透過Buffer 來儲存二進位的數據,而二進位的資料轉為字串的時候就需要指定字元集,Buffer 的from、byteLength、lastIndexOf 等方法都支援指定encoding:

具體支援的encoding 有這些:

utf8、ucs2、utf16le、latin1、ascii、base64、hex

可能有的同學會發現: base64、hex 不是字元集啊,怎麼也出現在這裡?

是的,位元組到字元的編碼方案除了字元集之外,也有用於轉為明文字元的 base64、以及轉為 16 進位的 hex。

這也是為什麼 Node.js 把它叫做 encoding 而不是 charset,因為支援的編解碼方案不只是字元集。

如果不指定 encoding,預設是 utf8。

const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');

console.log(buf.toString());// hello world

encoding 的原始碼

我去翻了下Node.js 關於encoding 的原始碼:

這一段是實作encoding 的:https: //github.com/nodejs/node/blob/master/lib/buffer.js#L587-L726

可以看到每個encoding 都實作了encoding、encodingVal、byteLength、write、slice、indexOf 這幾個api,因為這些api 用不同encoding 方案,會有不同的結果,Node.js 會根據傳入的encoding 來傳回不同的對象,這是一種多態的想法。

const encodingOps = {
  utf8: {
    encoding: 'utf8',
    encodingVal: encodingsMap.utf8,
    byteLength: byteLengthUtf8,
    write: (buf, string, offset, len) => buf.utf8Write(string, offset, len),
    slice: (buf, start, end) => buf.utf8Slice(start, end),
    indexOf: (buf, val, byteOffset, dir) =>
      indexOfString(buf, val, byteOffset, encodingsMap.utf8, dir)
  },
  ucs2: {
    encoding: 'ucs2',
    encodingVal: encodingsMap.utf16le,
    byteLength: (string) => string.length * 2,
    write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len),
    slice: (buf, start, end) => buf.ucs2Slice(start, end),
    indexOf: (buf, val, byteOffset, dir) =>
      indexOfString(buf, val, byteOffset, encodingsMap.utf16le, dir)
  },
  utf16le: {
    encoding: 'utf16le',
    encodingVal: encodingsMap.utf16le,
    byteLength: (string) => string.length * 2,
    write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len),
    slice: (buf, start, end) => buf.ucs2Slice(start, end),
    indexOf: (buf, val, byteOffset, dir) =>
      indexOfString(buf, val, byteOffset, encodingsMap.utf16le, dir)
  },
  latin1: {
    encoding: 'latin1',
    encodingVal: encodingsMap.latin1,
    byteLength: (string) => string.length,
    write: (buf, string, offset, len) => buf.latin1Write(string, offset, len),
    slice: (buf, start, end) => buf.latin1Slice(start, end),
    indexOf: (buf, val, byteOffset, dir) =>
      indexOfString(buf, val, byteOffset, encodingsMap.latin1, dir)
  },
  ascii: {
    encoding: 'ascii',
    encodingVal: encodingsMap.ascii,
    byteLength: (string) => string.length,
    write: (buf, string, offset, len) => buf.asciiWrite(string, offset, len),
    slice: (buf, start, end) => buf.asciiSlice(start, end),
    indexOf: (buf, val, byteOffset, dir) =>
      indexOfBuffer(buf,
                    fromStringFast(val, encodingOps.ascii),
                    byteOffset,
                    encodingsMap.ascii,
                    dir)
  },
  base64: {
    encoding: 'base64',
    encodingVal: encodingsMap.base64,
    byteLength: (string) => base64ByteLength(string, string.length),
    write: (buf, string, offset, len) => buf.base64Write(string, offset, len),
    slice: (buf, start, end) => buf.base64Slice(start, end),
    indexOf: (buf, val, byteOffset, dir) =>
      indexOfBuffer(buf,
                    fromStringFast(val, encodingOps.base64),
                    byteOffset,
                    encodingsMap.base64,
                    dir)
  },
  hex: {
    encoding: 'hex',
    encodingVal: encodingsMap.hex,
    byteLength: (string) => string.length >>> 1,
    write: (buf, string, offset, len) => buf.hexWrite(string, offset, len),
    slice: (buf, start, end) => buf.hexSlice(start, end),
    indexOf: (buf, val, byteOffset, dir) =>
      indexOfBuffer(buf,
                    fromStringFast(val, encodingOps.hex),
                    byteOffset,
                    encodingsMap.hex,
                    dir)
  }
};
function getEncodingOps(encoding) {
  encoding += '';
  switch (encoding.length) {
    case 4:
      if (encoding === 'utf8') return encodingOps.utf8;
      if (encoding === 'ucs2') return encodingOps.ucs2;
      encoding = StringPrototypeToLowerCase(encoding);
      if (encoding === 'utf8') return encodingOps.utf8;
      if (encoding === 'ucs2') return encodingOps.ucs2;
      break;
    case 5:
      if (encoding === 'utf-8') return encodingOps.utf8;
      if (encoding === 'ascii') return encodingOps.ascii;
      if (encoding === 'ucs-2') return encodingOps.ucs2;
      encoding = StringPrototypeToLowerCase(encoding);
      if (encoding === 'utf-8') return encodingOps.utf8;
      if (encoding === 'ascii') return encodingOps.ascii;
      if (encoding === 'ucs-2') return encodingOps.ucs2;
      break;
    case 7:
      if (encoding === 'utf16le' ||
          StringPrototypeToLowerCase(encoding) === 'utf16le')
        return encodingOps.utf16le;
      break;
    case 8:
      if (encoding === 'utf-16le' ||
          StringPrototypeToLowerCase(encoding) === 'utf-16le')
        return encodingOps.utf16le;
      break;
    case 6:
      if (encoding === 'latin1' || encoding === 'binary')
        return encodingOps.latin1;
      if (encoding === 'base64') return encodingOps.base64;
      encoding = StringPrototypeToLowerCase(encoding);
      if (encoding === 'latin1' || encoding === 'binary')
        return encodingOps.latin1;
      if (encoding === 'base64') return encodingOps.base64;
      break;
    case 3:
      if (encoding === 'hex' || StringPrototypeToLowerCase(encoding) === 'hex')
        return encodingOps.hex;
      break;
  }
}

總結

電腦中儲存資料的最小單位是位,但是儲存資訊最小的單位是字節,基於編碼和字元的映射關係又實現了各種字符集,包括ascii、iso、gbk 等,而國際標準化組織提出了unicode 來包含所有字符,unicode 實現方案有若干種:utf-8、utf-16、utf-24,他們分別用不同的字節數來儲存字元。其中 utf-8 是變長的,儲存體積最小,所以被廣泛應用。

Node.js 透過Buffer 儲存二進位數據,而轉為字串時需要指定編碼方案,這個編碼方案不只是包含字元集(charset),也支援hex、base64 的方案,包括:

utf8、ucs2、utf16le、latin1、ascii、base64、hex

我們看了下encoding 的Node.js 原始碼,發現每個編碼方案都會用實作一系列api,這是一種多態的思想。

encoding 是學習 Node.js 頻繁遇到的概念,而且 Node.js 的 encoding 不只是包含 charset,希望這篇文章能幫大家了解程式設計和字元集。

更多程式相關知識,請造訪:程式設計入門! !

以上是聊聊Node.js Buffer中的encoding(程式設計)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除