javascript 類型數組讀取二進制數據


先建一個文件,按UTF-16大端 BOM 格式保存一個字符串:hi aleck,

使用 file API 把他按二進制方式讀取到瀏覽器。

文件讀取方法在這里:

http://hi.baidu.com/ecalf830/item/e3b2d2c9b1003222a0b50a39

簡單介紹一下 file api 的相關用法

1、在瀏覽器中打開文件

<input type="file" id="f" multiple="multiple"/> 

當 file 控件 有 mutiple 屬性時,可以上傳多個文件,文件打開后保存在 file 控件的  files 屬性,這個例子中只打開一個文件,獲取的文件的方式是 :

var file = document.getElementById("f").files[0];

 

2、Blob 與文件讀取類 FileReader 

二進制對象 Blob (binary large object) 用於存儲二進制字節數據,

創建一個 blob 對象: var blob  = new Blob();  該對象的size屬性表示文件的字節數,類似於數組的length,slice 可以從文件中復制出一段二進制數據,用法類似於數組的 silce,復制得到的數據也是 Blob 對象。

FileReader 類用於讀取 Blob  數據對象。

創建一個文件讀取 對象 :var reader = new FileReader();

瀏覽器打開的文件 file 繼承於 Blob (thefile instanceof Blob == true),因此可以使用 FileReader  打開 file 對象。

 

FileReader 對象的屬性、方法及在打開Blob數據過程中可以使用的回調函數(試在 firebug 下查看 FileReader 的屬性和方法 console.log(new FileReader())):

    error: null

    onabort: null

    onerror: null

    onload: null //讀取成功調用

    onloadend: function(){...} //讀取完畢調用,即使讀取失敗也會調用

    onloadstart: null //開始打開文件時調用

    onprogress: null  //文件打開過程中,反復調用直至文件讀取完畢

    readyState: 2

    result: "þÿhi aleck" //文件讀取結果得到的字符串

    __proto__: FileReader

    DONE: 2

    EMPTY: 0

    LOADING: 1

    abort: function abort() { [native code] }

    addEventListener: function addEventListener() { [native code] }

    constructor: function FileReader() { [native code] }

    dispatchEvent: function dispatchEvent() { [native code] }

    readAsArrayBuffer: function readAsArrayBuffer() { [native code] }  //讀取為二進制字節緩沖

    readAsBinaryString: function readAsBinaryString() { [native code] } //讀取為二進制數據

    readAsDataURL: function readAsDataURL() { [native code] } //讀取為base64數據

    readAsText: function readAsText() { [native code] } //讀取為普通字符串

    removeEventListener: function removeEventListener() { [native code] }

    __proto__: Object
 

可以在 onloadend 事件內綁定處理打開的文件數據的回調函數,然后打開文件,

這里將文件以二進制方式打開:reader.readAsBinaryString();

查 看 reader.result 可以看到讀取結果。由於javascript 沒有二進制數據類型,因此,二進制數據按每個字節的8位二進制數對應 的 unicode 編碼的字符顯示出來,因此二進制數據實際被顯示為一串 ascii 字符,如果要讀取的文件是英文格式的, 那么這個二進制數據看起 來跟實際的字符串很相似。

本例中 UTF-16 BOM 大端格式的 字符串  "hi aleck" 打開為二進制后得到:

 reader.result = "þÿhi aleck" //注意 reader.result.length 為 18, 這其中包含了空字符 \0 和標記字節流存儲順序的“零寬空格”

 

每個字節的8位二進制轉為二位十六進制為:

 reader.result.split('').map(function(v){ return  ('0'+v.charCodeAt(0).toString(16)).slice(-2); })

得到:

 

["fe", "ff", "00", "68", "00", "69", "00", "20", "00", "61", "00", "6c", "00", "65", "00", "63", "00", "6b"]

 

由於是UTF-16編碼,每個字符占二字節,feff 是用於字節序標記的字符"零寬空格",其余字符編碼由於只需要一個字節,高位字節用 0 補足,unicode 編碼為0 的字符為空字符 \0 。

例如 0068 是字母 h 的 2 字節(16位二進制轉4位16進制)編碼格式:

 

String.fromCharCode(parseInt('0068',16)) //h

 

3、字節數組緩沖 ArrayBuffer 和 類型數組

 ArrayBuffer 類的對象指向一段定長的內存空間,其屬性 byteLength  表示對象的字節長度,slice 對象類似於 Blob 和數組的 slice 用於拷貝出一定字節長度的緩沖數據並創建成新的 ArrayBuffer 對象。

ArrayBuffer 緩沖字節數據對象只是一段內存空間,不能直接訪問,需要通過 DataView 對象(DataView 有點類似一個混合類型數組的元素組成的 list,其元素可以是幾種類型數組中的任一種,這里不細述)或類型數組訪問內存的數據,他們的關系有點像 Blob 與 FileReader .

其中 javascript 類型數組有下面幾種:

Float32Array 

Float64Array 

Int8Array

Int16Array 

Int32Array 

Uint8Array 

Uint16Array 

Uint32Array 

Uint8ClampedArray
 可以訪問下面的網站了解這些數據類型

http://www.javascripture.com/DataView

 

本例中使用存儲無符號整數的 Uint8Array、 Uint16Array、 Uint32Array 

Uint8Array 每個元素為一字節,表達范圍為 0-255,Uint16Array 每元素 2字節,Uint32Array 每個元素4字節。

如果想要將 緩沖字節 ArrayBuffer 對象讀入到類型數組中,比如可以這樣

var buff = new ArrayBuffer(16); //創建一段16字節大小的內存緩沖空間

var u8 = new Uint8Array(buff,8); //創建一個8位無符號整形類型數組,並指向 buff 對象的前 8 個字節。

var u16 = new Uint16Array(buff,8); //16 位類型數組

var u32 = new Uint32Array(buff,8); //32為類型數組

組要注意的是,類型數組並沒有拷貝出 ArrayBuffer 對象的內容,而是指向對應的內存空間,因此 u8、u16、u32 這幾個數組訪問的是同一塊內存,這意味着通過 u8 修改了內容后,u16 和 u32 將訪問到改變后的內容。

現在 u8、u16 、u32 指向同一塊 8 字節長度的內存,因此 u8 有8 個元素,u16 有4個元素,u32 則只有2個元素。

 

將上面打開文件得到的字節的前8個通過u8數組逐個存入 buff 內

 

["fe","ff","00","68","00","69","00","20"].forEach(function(v,i){  u8[i] = parseInt(v,16);});

 

有:

u8 = [254, 255, 0, 104, 0, 105, 0, 32]

u16 =  [65534, 26624, 26880, 8192]

u32 = [1744895998, 536897792]
 
 查看數組的元素:
('00'+u8[0].toString(16)).slice(-2) // "fe"
('00'+u8[1].toString(16)).slice(-2) // "ff"
('00'+u8[2].toString(16)).slice(-2) // "00"
('00'+u8[3].toString(16)).slice(-2) // "68"
('00'+u8[4].toString(16)).slice(-2) // "00"
('00'+u8[5].toString(16)).slice(-2) // "69"
('00'+u8[6].toString(16)).slice(-2) // "00"
('00'+u8[7].toString(16)).slice(-2) // "20"

 
('0000'+u16[0].toString(16)).slice(-4) // "fffe"
('0000'+u16[1].toString(16)).slice(-4) // "6800"
('0000'+u16[2].toString(16)).slice(-4) // "6900"

('0000'+u16[3].toString(16)).slice(-4) // "2000"

 
('00000000'+u32[0].toString(16)).slice(-8) // "6800fffe"
('00000000'+u32[1].toString(16)).slice(-8) // "20006900"
 

可以看到,讀取 Blob 對象時,在多字節類型數組中( u16 2字節的元素 和 u32 4字節的元素), 元素的字節是按低位在前的順序存儲的,即小端方式。

 我們也可以直接把通過 FileReader 的 readAsArrayBuffer() 方法將 Blob 對象讀取到 ArrayBuffer 對象中,然后在 類型數組中訪問字節:

reader.readAsArrayBuffer(blob);

var u16 = new Uint16Array(reader.result); 

u16 為 [65534, 26624, 26880, 8192, 24832, 27648, 25856, 25344, 27392]
 

類型數組的數據溢出問題:

因 為數組元素類型決定其在內存中占據的字節長度,其數值表達范圍是按不同類型是不同的,如果存儲的數值超出其表達范圍便發生溢出,例 如, Uint8Array 類型的數組元素只占據一個字節,元素值的表達范圍是 8位2進制, 即 0~255 ,如果試圖存儲 257 將發生溢出, 實際會得到取模后的值

var uint8 = new Uint8Array(10);
uint8[0] //0
uint8[0]=257; 

uint8[0]; //1  257%256 = 1
uint8[0] = -1;
uint8[0]; //255   -1+256=255

uint8[0] = x;  則   uint8[0] == (x%256+256)%256 ; //true

 
Uint8ClampedArray 跟 Uint8Array 不同,如果給 Uint8ClampedArray 類型的數組元素賦值超出范圍,則取最靠近所賦值的合法的值,
這個數組可以用於處理圖像的顏色數據,例如 canvas 的 image.data
 
          
var context = document.createElement("canvas").getContext("2d");

var imageData = context.createImageData(16, 16);

console.log(imageData); //ImageData {height: 16, width: 16, data: Uint8ClampedArray[1024]}

console.log(imageData.data instanceof Uint8ClampedArray)//true
 
          

 

 
          
var cint8 = new Uint8ClampedArray(10);  // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
cint8[0]=257; // [255, 0, 0, 0, 0, 0, 0, 0, 0, 0]
cint8[0]= -2; //[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

cint8[0] = x;則 cint8[0] == Math.min(Math.max(0,x),255)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM