精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

二進制數據在JS程序里的表達

冬至子 ? 來源:劉驥 ? 作者:劉驥 ? 2023-07-07 14:46 ? 次閱讀

因為隨著XHR2和現代瀏覽器的普及,在瀏覽器當中處理二進制不再向過去那樣無所適從,隨著Canvas/WebGL等新技術逐漸開始進入大眾視野,也會用到一些字節數組或者16位、8位整數等東西。在node.js剛剛發布的4.0版本中,Buffer的底層使用了更符合JS標準的Uint8Array來實現,瀏覽器和node.js再次向相同的目標靠近了一點點,所以對于JS中處理二進制,我就打算寫這篇文章作一個入門性質的流水賬,方便一些對二進制處理不了解的同學快速入門,雖然在前端領域用到的不多,不過也可以作為茶余飯后的休閑談資。

二進制數據在JS程序里的表達

現今世界上幾乎所有的計算機體系結構都是以字節(byte)為二進制數據的基本單位(注:不是說最小單位),所以二進制常常以字節數組的形式存在于程序當中。例如在C#里面,就用byte[],標準C里面沒有byte類型,但可以通過typedef把byte定義為unsigned char的別名,效果是一樣的。

JS設計之初似乎根本沒想過要處理二進制的東西,加上對類型的極度弱化,對于字節的概念可以說是非常非常的模糊。如果要表達字節數組,那么似乎只能用一個普通數組來表示。

HTML5體系引入了一大堆新的東西,比如XHR2,是可以上傳或下載二進制內容的,與之配套的東西就是JS里的ArrayBuffer和Typed Array了。

ArrayBuffer是一個固定長度的字節序列,你可以通過new ArrayBuffer(length)來得到一片空間,或者用下文將會介紹的方法從XHR2等途徑獲取。由于內部實現與數組不一樣,ArrayBuffer通常都是連續內存(注意,這只是經驗之談,并不是規范也不是文檔所明確的),因此對于高密度的訪問操作而言它比JS中的Array速度會快很多(但并不要用它來簡單地代替Array)。如果用Chrome的Profile工具查看Heap Snapshot,會發現ArrayBuffer會被單獨列為一類,也許它的內存分配和布局與Array以及其他JS對象有一些差別吧。

ArrayBuffer是不能直接被訪問的,因此需要借助Typed Array。Typed Array是一組具體數據類型的Array-Like類型的統稱,包括

Int8Array 8位有符號整數,類似于C里面的char

Uint8Array 8位無符號整數,類似于C里面的unsigned char

Uint8ClampedArray 8位無符號整數,跟Uint8類似,但在溢出處理上不大一樣

Int16Array 后面這些類型就不羅嗦了

Uint16Array

Int32Array

Uint32Array

Float32Array

Float64Array

Typed Array的背后是一個ArrayBuffer,也就是說,事實上的數據是存在ArrayBuffer里面的,而Typed Array只是給你提供了一個某種類型的讀寫接口,用MDN的話說,叫做

Multiple views on the same data

舉個栗子,如果我們有一個ArrayBuffer名為buffer(先不考慮怎么構造這個測試數據),內容如下:

01 02 03 04 05 06 07 08

也就是說它有8個字節,我們分別用它來構造Uint8Array, Uint16Array, Uint32Array,則可以得到

var u8 = new Uint8Array(buffer); // length為8

var u16 = new Uint16Array(buffer); // length為4

var u32 = new Uint32Array(buffer); // length為2

它們的內容分別為

[1, 2, 3, 4, 5, 6, 7, 8]

[513, 1027, 1541, 2055]

[67305985, 134678021]

這不難理解。

可以看出,如果要手工構造上面的測試數據ArrayBuffer,用Uint8Array就會很方便(呃事實上這是我個人最常用的一種Typed Array)。

而如果用同樣的ArrayBuffer構建帶符號整數類型,則可能因為整數溢出而得到不同的結果,上面的例子并沒有碰到,有興趣的話可以自己試試。因此使用Typed Array也可以用來做有符號數和無符號數的轉換。

如果你用過canvas的getImageData/putImageData的話,會發現它給你的就是一個Uint8ClampedArray,這東西訪問起來速度比JS的原生Array快很多,使得對canvas進行高速的像素操作成為可能。

然而最最重要的一個概念還是:Typed Array不直接存放任何數據,所有對Typed Array進行讀寫的操作,最終都會落實到它背后所持有的ArrayBuffer的身上。ArrayBuffer才是真正的raw bytes,而Typed Array只是一個操作窗口/操作視圖(View)。

獲取二進制數據

nodejs那邊先按住不表,這里談談在網頁里如何獲取二進制數據?常見的辦法有3種,1是通過XMLHttpRequest 2,2是通過File和Blob一套相關接口。

通過XMLHttpRequest 2

XHR2的接口跟XHR幾乎是一樣的,當制定xhr.responseType = 'arraybuffer'以后,在成功獲取數據的回調里就可以通過xhr.response來得到請求結果的ArrayBuffer了,然后就可以按照你的意愿來構造各種Typed Array進行訪問。

responseType還可以有blob取值,可以用xhr.response獲得Blob對象。

通過File和Blob

在HTML5中提供了對表單的文件控件[ ] 更豐富的操作,可以通過inputDOM對象的.files來獲取一個FileList,當然通常瀏覽器都只提供了單選的文件控件,于是這里都只會有一個File對象。另外,通過拖拽、剪貼板等方式也能獲取到File或者Blob。

File繼承了Blob,并提供了name, lastModifiedDate等基礎元數據,但是依然是一個深度封裝,不能直接獲取到它的二進制。

Blob是Binary large object的縮寫,它與ArrayBuffer的區別是除了raw bytes以外它還提供了mime type作為元數據。但它依然是無法直接被讀寫的。

這時候需要借助FileReader的幫忙。FileReader提供了一組用來將Blob讀取為更為實用的類型的方法

readAsArrayBuffer()

readAsBinaryString()

readAsDataURL()

readAsText()

例如

var file = get_file_some_how();

var fr = new FileReader();

fr.onload = function(e) {

e.target.result; // 讀取的結果

};

fr.readAsDataUrl(file); // readAsArrayBuffer

可以干什么呢?例如圖片上傳之前的本地預覽(甚至基于canvas的編輯)等等都可以實現了。

Blob的其他構造方法多而雜,這里就先不到處搬運文檔了。

消費二進制數據

何謂消費?最常見的方式也許就是通過XHR2直接把二進制數據以文件方式POST到服務端去。

這里我比較推薦使用FormData來構造POST數據。因為在服務端收的時候會比較容易一些,具體有興趣可以去找找別人的例子。

雖然直接提交ArrayBuffer也是可以的,但是這種時候服務端收到的POST body會是一大團,用起來不方便。如果要使用FormData來提交ArrayBuffer,需要先將其構造成Blob。

對Typed Array的構造留個心眼

當使用new xxxxxArray(arrayBuffer)這個重載進行構造的時候,它會默認基于此ArrayBuffer進行構造。但當使用new xxxxArray(another_typed_array)這個重載的時候,則是進行“拷貝構造”,這樣兩個Typed Array會指向不同的buffer,需要注意這是否符合預期。

如果需要基于同一個ArrayBuffer來構造Typed Array,可以使用Typed Array的buffer, byteLength,byteOffset來獲取它背后的ArrayBuffer。

Tips(坑)

對內存對齊留個心眼

當使用ArrayBuffer來構造Typed Array的時候,可以指定byteOffset參數,例如

var buffer = get_array_buffer_some_how();

var i16 = new Int16Array(buffer, 10);

上面的代碼就能以buffer向后偏移10字節處為起點來構造Int16Array,但是如果將10設置為一個奇數,會發現如下錯誤:

RangeError: start offset of Int16Array should be a multiple of 2

這是因為Typed Array對內存對齊有要求,它不能在非對齊的位置建立,同理,Uint32Array和Int32Array則要求偏移量是4字節對齊的。

因此如果你希望在非對齊的位置進行讀寫,則需要借助DataView的幫忙。

對字節序留個心眼

我們日常中所寫的程序,幾乎都不需要關心字節序,因此這個問題沒那么嚴重,知道自己的程序會有字節序問題的人,開發到這里也肯定會知道問題的存在,但這里還是稍微提一下。

按照MDN的說法,Typed Array只會使用當前平臺的字節序,例如我們現在用的桌面電腦不論PC還是Mac都是x86/x64的,也就是little-endian了。

使用DataView,不僅可以解決上面說到的內存對齊的問題,還可以指定讀寫時的字節序,具體參數都在文檔里面了,就不搬運了。

使用DataView配合Typed Array也可以做到一個檢測當前平臺字節序的技巧:

function isLittleEndian() {

var buf = new ArrayBuffer(2);

var view = new DataView(buf);

view.setInt16(0, 256, true);//顯式以little endian寫入數據

// 此時buf里的內存布局應該是 00 01

var i16 = new Int16Array(buf);

// 如果以little endian讀取,它就是256;以big endian讀取,則是1

return (i16[0] === 256);

}

如果你編寫的程序需要垮體系結構例如x86/ARM/PPC等,則在交換文件和網絡包的時候需要謹慎處理字節序,當然一個辦法是在這些地方預先規范統一字節序以防后患。不過那些都是題外話了。

小結

使用ArrayBuffer來存儲一段字節,使用Typed Array來構建一個具體數值類型的訪問窗口,使用DataView對非對齊或在乎字節序的ArrayBuffer進行更精確的操作,使用XHR2, Blob, File, FileReader, FormData等多種方式來獲取或消費ArrayBuffer。

另外羅嗦一句,瀏覽器還提供了一系列所謂的“Binary String”,就是一些看起來像亂碼一樣的字符串,然后又提供了atob/btoa這種方式來對Base64和“Binary String”進行相互轉換,甚至FileReader還提供了readAsBinaryString方法(已經廢棄了,善哉)。這個Binary String真是誰用誰遭殃,別問我為什么知道……

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 存儲器
    +關注

    關注

    38

    文章

    7453

    瀏覽量

    163608
  • 二進制
    +關注

    關注

    2

    文章

    794

    瀏覽量

    41600
  • MDN
    MDN
    +關注

    關注

    0

    文章

    6

    瀏覽量

    8072
  • HTML5
    +關注

    關注

    0

    文章

    73

    瀏覽量

    17372
收藏 人收藏

    評論

    相關推薦

    二進制相對調相(二進制差分調相2DPSK)的工作原理

    二進制相對調相(二進制差分調相2DPSK)的工作原理
    發表于 10-21 13:01 ?3189次閱讀
    <b class='flag-5'>二進制</b>相對調相(<b class='flag-5'>二進制</b>差分調相2DPSK)的工作原理

    二進制

    二進制   二進制與十進制的區別在于數碼的個數和進位規律有很大的區別,顧名思義,二進制的計數規律為逢二進一,是以2為基數的計數體制。10這
    發表于 04-06 23:48 ?8168次閱讀
    <b class='flag-5'>二進制</b>

    二進制編碼和二進制數據

    二進制編碼和二進制數據   二進制編碼是計算機內使用最多的碼制,它只使用兩個基本符號"0"和"1",并且通過由這兩個符號組成的
    發表于 10-13 16:22 ?4762次閱讀

    什么是二進制計數器,二進制計數器原理是什么?

    什么是二進制計數器,二進制計數器原理是什么? 計數器是數字系統中用得較多的基本邏輯器件。它不僅能記錄輸入時鐘脈沖的個數,還可以實現
    發表于 03-08 13:16 ?3.1w次閱讀

    二進制電平,什么是二進制電平

    二進制電平,什么是二進制電平 二進制數字通信系統中,每個碼元或每個符號只能是“1”和“0”兩個狀態之一。若將每個碼元可能取的狀態增
    發表于 03-17 16:51 ?2348次閱讀

    進制數的二進制編碼

    進制數的二進制編碼     人機交互過程中,為了既滿足系統中使用二進制數的要求,又適應人們使用十進制
    發表于 05-02 19:04 ?9733次閱讀

    二進制加法程序【匯編版】

    二進制加法程序【匯編版】二進制加法程序【匯編版】二進制加法程序【匯編版】
    發表于 12-29 11:02 ?0次下載

    二進制加法程序【C語言版】

    二進制加法程序【C語言版】二進制加法程序【C語言版】二進制加法程序【C語言版】
    發表于 12-29 11:03 ?0次下載

    二進制數據壓縮算法

    二進制數據壓縮算法二進制是計算技術中廣泛采用的一種數制。二進制數據是用0和1兩個數碼來表示的數。它的基數為2,進位規則是“逢
    的頭像 發表于 02-28 09:31 ?2w次閱讀

    如何使用二進制程序

    二進制來寫程序這么反人類的事情,的確是很裝的事情,但是它不但是一件很裝的事情,也是掌握底層知識的基礎能力之一。聽我慢慢道來。
    的頭像 發表于 01-20 11:50 ?1.1w次閱讀

    二進制加法的實現細節

    之前的文章,曾經多次提到過二進制加法的數字電路,這里詳細說說它的細節。
    的頭像 發表于 03-23 13:58 ?2595次閱讀
    <b class='flag-5'>二進制</b>加法的實現細節

    二進制數據及取值范圍的計算方法

    本文介紹二進制數據的相關知識,如定義、取值范圍計算、轉換為十進制的方法以及一些常見位數的二進制數據的取值范圍等。
    的頭像 發表于 11-08 15:48 ?2027次閱讀
    <b class='flag-5'>二進制</b><b class='flag-5'>數據</b>及取值范圍的計算方法

    二進制、八進制、十六進制現實當中有什么意義?

    二進制、八進制、十六進制現實當中有什么意義? 二進制、八進制和十六
    的頭像 發表于 01-16 11:14 ?5117次閱讀

    如何實現二進制和BCD碼數據的相互轉變?

    如何實現二進制和BCD碼數據的相互轉變? 二進制碼是將十進制數字表示為二進制數和十進制數的一種表
    的頭像 發表于 02-18 14:51 ?3309次閱讀

    二進制編碼器工作原理 如何選擇二進制編碼器

    二進制編碼器是一種數字電路,它將輸入的二進制代碼轉換為對應的輸出信號。在數字系統中,編碼器用于將數據從一種形式轉換為另一種形式,以便于處理和傳輸。 二進制編碼器工作原理 輸入與輸出關系
    的頭像 發表于 11-06 09:44 ?306次閱讀