JS幻想 讀取二進(jìn)制文件第1/2頁
更新時間:2009年04月20日 15:25:34 作者:
如果說讓JavaScript讀取站點(diǎn)上一文本文件,那不過是個再簡單不了的事了;但若說要換成一個二進(jìn)制的文件,并且是完全靜態(tài)的讀取,那似乎有點(diǎn)天方夜譚了。
且不說瀏覽器內(nèi)置的HTTP插件是否支持二進(jìn)制數(shù)據(jù)流,就JavaScript其自身就毫無二進(jìn)制的處理能力。聰明的讀者也許想說用VBScript就可以實(shí)現(xiàn)了。不錯,因?yàn)閂BScript,IE,ActiveX都是微軟的產(chǎn)物,所以他們有著無縫的結(jié)合。IE的HTTP組件確實(shí)能夠讀取二進(jìn)制數(shù)據(jù),而且也只能夠讓VBScript讀取。但對于其他瀏覽器,就束手無策了。
畢竟腳本的理念僅僅是用來處理一些簡單的交互的,對于處理字節(jié)流之類的復(fù)雜問題完全不該是腳本的職責(zé)。不過作為一種探索,我們還是可以挖掘下其中的樂趣。當(dāng)然,首先要明確的是,對于二進(jìn)制的讀取JS確實(shí)是無能為力的,不過我們可以來模擬,以達(dá)到相同的效果,下面就跟著我來吧。
比如現(xiàn)在想要做個推箱子的小游戲,共200關(guān)。這時唯一值得考慮到事就出現(xiàn)了:把這200關(guān)地圖數(shù)據(jù)保存在何處?如果直接硬塞在一個腳本文件里似乎顯得太大,或者單獨(dú)保存在一個文件里,但是用什么格式。。。不過對于推箱子游戲來說簡單的文本格式也夠了,但對于一些地圖較復(fù)雜的也許就會使用BASE64編碼,然后由客戶端的HTTP組件下載下來解碼使用。BASE64編碼在JS中還是很常用的,畢竟它不受任何的環(huán)境限制,能夠處理字符串就行。
既然有個BASE64,那為什么就不能有BASE128,BASE256了呢?如果能實(shí)現(xiàn)“BASE256”,豈不就是二進(jìn)制字節(jié)流了?如果真可以如此,那這種方法早就流傳開了,還留著那么多的BASE64做什么:)畢竟這是字符串,而不叫字節(jié)串,那肯定是有區(qū)別的。不妨把一個二進(jìn)制的文件,當(dāng)作文本文件讀取回來試試,很快你就會發(fā)現(xiàn)超過一旦文件中出現(xiàn)127(0x7F)以上的字符,馬上就出錯了;如果存在個0x00字節(jié)的話,后面的內(nèi)容都會蕩然無存,這意味著256個字符中能夠利用的還不到一半。
然而,可別忘了這個測試使用的僅僅是最基本的ASCII編碼,對于功能強(qiáng)大的XMLHTTP支持的也絕不僅限于如此,那么就試試Unicode字符會怎樣。在記事本隨便輸幾個字符,保存為Unicode格式的文本文件。這時用XMLHTTP讀取,顯示出來的與記事本里的一模一樣,但是再用16進(jìn)制編輯器打開此文件時,就大不相同了。在文件的開頭出現(xiàn)了FF FE兩字節(jié),后面的每個內(nèi)容都是由一個0隔開。畢竟這是16位的Unicode字符,除了基本的ASCII外,還要保存各國的文字。例如一個中文就占用了2個字節(jié),而英文數(shù)字仍然占用2字節(jié),只是高位由0填充罷了(注意高位字節(jié)是在低位字節(jié)后面的)。
XMLHTTP能夠成功顯示出來就說明Unicode還是支持的?,F(xiàn)在試著修改文件里面的數(shù)據(jù),看看超過了那些范圍才會出錯。把數(shù)據(jù)修改成如下:FFFE 0001 0203 7F00 8000 8100 FF00 FFFF。用XMLHTTP測試,雖然顯示的都是亂碼,但并沒出錯,返回的字符串用charCodeAt(i)及toString(16)方法一試,原形畢露!幾經(jīng)測試,Unicode并不像ASCII那樣有范圍限制,但唯獨(dú)一個例外:0x0000!
眾所周知,0x00就是ASCII的結(jié)束標(biāo)志。但到了Unicode的世界里一切都是16位的,因此字符尾也成了0x0000。到了這里似乎有點(diǎn)遺憾,但接著的目標(biāo)很明確:如果能夠去掉文件中的0x0000,并在之前加上0xFEFF,就能夠讓JavaScript讀取了。
去掉以及恢復(fù),不妨就稱他編碼與解碼吧。編碼的方法就見智見仁了,最簡單的辦法就是記錄下每個0x0000的位置,然后除去;在客戶端按照記錄的位置再復(fù)原回去。雖然簡單,但也別忘了,0x00在二進(jìn)制文件中是相當(dāng)多的,即便是0x0000也是。這樣光是記錄他們的內(nèi)容就有很多,顯然不是很好。既然說到要記錄,為何一定要記錄0x0000的位置?反過來想,我們應(yīng)該記錄這個文件中出現(xiàn)次數(shù)最少的字符,以及它的位置,然后把0x0000的地方替換成這個字符;解碼的時候一旦出現(xiàn)這個字符,但當(dāng)前位置又不在記錄中,就可以確定這就是個0x0000。事實(shí)上,在64K以下的文件中肯定有個字符根本就不會出現(xiàn)的(為什么仔細(xì)考慮下就明白),即使是在64K以上,還是有非常多的文件不存在某個字符的。畢竟一個Unicode字符有65536之多,很少有文件會把他們?nèi)加蒙?,除非是個冗余極小的壓縮文件,但也不會很多。
到此,編碼解碼思路已明了,剩下自然是實(shí)現(xiàn)他們。
剛才提到了源文件中出現(xiàn)最少(甚至是沒有)的Unicode字符,不妨就稱作key
首先來定義新生成的二進(jìn)制文件頭格式:
00 01 0xFEFF。 Unicode文件頭,這是必須的
02 03 Key值。 為了不讓0x0000成為Key,在尋找的過程中忽略0x0000
03 04 Key出現(xiàn)的次數(shù)+1。 +1是為了避免該位置出現(xiàn)0x0000,后面的也都一樣
05 06
07 08 第1個Key的位置 用4字節(jié)保存每個Key的位置。
09 0A
0B 0C 。。。
0D 0E
0F 10 第n個Key的位置
11 12 文件數(shù)據(jù)內(nèi)容。。。
畢竟腳本的理念僅僅是用來處理一些簡單的交互的,對于處理字節(jié)流之類的復(fù)雜問題完全不該是腳本的職責(zé)。不過作為一種探索,我們還是可以挖掘下其中的樂趣。當(dāng)然,首先要明確的是,對于二進(jìn)制的讀取JS確實(shí)是無能為力的,不過我們可以來模擬,以達(dá)到相同的效果,下面就跟著我來吧。
比如現(xiàn)在想要做個推箱子的小游戲,共200關(guān)。這時唯一值得考慮到事就出現(xiàn)了:把這200關(guān)地圖數(shù)據(jù)保存在何處?如果直接硬塞在一個腳本文件里似乎顯得太大,或者單獨(dú)保存在一個文件里,但是用什么格式。。。不過對于推箱子游戲來說簡單的文本格式也夠了,但對于一些地圖較復(fù)雜的也許就會使用BASE64編碼,然后由客戶端的HTTP組件下載下來解碼使用。BASE64編碼在JS中還是很常用的,畢竟它不受任何的環(huán)境限制,能夠處理字符串就行。
既然有個BASE64,那為什么就不能有BASE128,BASE256了呢?如果能實(shí)現(xiàn)“BASE256”,豈不就是二進(jìn)制字節(jié)流了?如果真可以如此,那這種方法早就流傳開了,還留著那么多的BASE64做什么:)畢竟這是字符串,而不叫字節(jié)串,那肯定是有區(qū)別的。不妨把一個二進(jìn)制的文件,當(dāng)作文本文件讀取回來試試,很快你就會發(fā)現(xiàn)超過一旦文件中出現(xiàn)127(0x7F)以上的字符,馬上就出錯了;如果存在個0x00字節(jié)的話,后面的內(nèi)容都會蕩然無存,這意味著256個字符中能夠利用的還不到一半。
然而,可別忘了這個測試使用的僅僅是最基本的ASCII編碼,對于功能強(qiáng)大的XMLHTTP支持的也絕不僅限于如此,那么就試試Unicode字符會怎樣。在記事本隨便輸幾個字符,保存為Unicode格式的文本文件。這時用XMLHTTP讀取,顯示出來的與記事本里的一模一樣,但是再用16進(jìn)制編輯器打開此文件時,就大不相同了。在文件的開頭出現(xiàn)了FF FE兩字節(jié),后面的每個內(nèi)容都是由一個0隔開。畢竟這是16位的Unicode字符,除了基本的ASCII外,還要保存各國的文字。例如一個中文就占用了2個字節(jié),而英文數(shù)字仍然占用2字節(jié),只是高位由0填充罷了(注意高位字節(jié)是在低位字節(jié)后面的)。
XMLHTTP能夠成功顯示出來就說明Unicode還是支持的?,F(xiàn)在試著修改文件里面的數(shù)據(jù),看看超過了那些范圍才會出錯。把數(shù)據(jù)修改成如下:FFFE 0001 0203 7F00 8000 8100 FF00 FFFF。用XMLHTTP測試,雖然顯示的都是亂碼,但并沒出錯,返回的字符串用charCodeAt(i)及toString(16)方法一試,原形畢露!幾經(jīng)測試,Unicode并不像ASCII那樣有范圍限制,但唯獨(dú)一個例外:0x0000!
眾所周知,0x00就是ASCII的結(jié)束標(biāo)志。但到了Unicode的世界里一切都是16位的,因此字符尾也成了0x0000。到了這里似乎有點(diǎn)遺憾,但接著的目標(biāo)很明確:如果能夠去掉文件中的0x0000,并在之前加上0xFEFF,就能夠讓JavaScript讀取了。
去掉以及恢復(fù),不妨就稱他編碼與解碼吧。編碼的方法就見智見仁了,最簡單的辦法就是記錄下每個0x0000的位置,然后除去;在客戶端按照記錄的位置再復(fù)原回去。雖然簡單,但也別忘了,0x00在二進(jìn)制文件中是相當(dāng)多的,即便是0x0000也是。這樣光是記錄他們的內(nèi)容就有很多,顯然不是很好。既然說到要記錄,為何一定要記錄0x0000的位置?反過來想,我們應(yīng)該記錄這個文件中出現(xiàn)次數(shù)最少的字符,以及它的位置,然后把0x0000的地方替換成這個字符;解碼的時候一旦出現(xiàn)這個字符,但當(dāng)前位置又不在記錄中,就可以確定這就是個0x0000。事實(shí)上,在64K以下的文件中肯定有個字符根本就不會出現(xiàn)的(為什么仔細(xì)考慮下就明白),即使是在64K以上,還是有非常多的文件不存在某個字符的。畢竟一個Unicode字符有65536之多,很少有文件會把他們?nèi)加蒙?,除非是個冗余極小的壓縮文件,但也不會很多。
到此,編碼解碼思路已明了,剩下自然是實(shí)現(xiàn)他們。
剛才提到了源文件中出現(xiàn)最少(甚至是沒有)的Unicode字符,不妨就稱作key
首先來定義新生成的二進(jìn)制文件頭格式:
復(fù)制代碼 代碼如下:
00 01 0xFEFF。 Unicode文件頭,這是必須的
02 03 Key值。 為了不讓0x0000成為Key,在尋找的過程中忽略0x0000
03 04 Key出現(xiàn)的次數(shù)+1。 +1是為了避免該位置出現(xiàn)0x0000,后面的也都一樣
05 06
07 08 第1個Key的位置 用4字節(jié)保存每個Key的位置。
09 0A
0B 0C 。。。
0D 0E
0F 10 第n個Key的位置
11 12 文件數(shù)據(jù)內(nèi)容。。。
相關(guān)文章
javascript讀取xml實(shí)現(xiàn)javascript分頁
這篇文章主要介紹了javascript讀取xml數(shù)據(jù)對其實(shí)現(xiàn)javascript分頁效果,大家參考使用吧2013-12-12layer.msg()去掉默認(rèn)時間,實(shí)現(xiàn)手動關(guān)閉的方法
今天小編就為大家分享一篇layer.msg()去掉默認(rèn)時間,實(shí)現(xiàn)手動關(guān)閉的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09TypeScript中的類型斷言[as語法|<>語法]的使用
本文主要介紹了TypeScript中的類型斷言[as語法|<>語法]的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06在 webpack 中使用 ECharts的實(shí)例詳解
這篇文章主要介紹了在 webpack 中使用 ECharts的實(shí)例代碼,需要的朋友可以參考下2018-02-02javascript tips提示框組件實(shí)現(xiàn)代碼
一個簡單的類似title的提示效果,但現(xiàn)實(shí)內(nèi)容可以很豐富,以上js另存為tip.js,下面是使用的demo。2010-11-11深入理解JavaScript中async/await的錯誤處理方式
在現(xiàn)代JavaScript開發(fā)中,異步編程是不可或缺的一部分,async和await是一種強(qiáng)大的異步編程工具,它們使得編寫和維護(hù)異步代碼更加容易和清晰,然而,異步操作仍然可能會出現(xiàn)錯誤,本文將深入探討async和await的錯誤處理方式,提供詳細(xì)的代碼示例和解釋2023-09-09