最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

JS幻想读取二进制文件_javascript技巧

来源:懂视网 责编:小采 时间:2020-11-27 20:42:37
文档

JS幻想读取二进制文件_javascript技巧

JS幻想读取二进制文件_javascript技巧:如果说让JavaScript读取站点上一文本文件,那不过是个再简单不了的事了;但若说要换成一个二进制的文件,并且是完全静态的读取,那似乎有点天方夜谭了。且不说浏览器内置的HTTP插件是否支持二进制数据流,就JavaScript其自身就毫无二进制的处理能力。聪明的读者
推荐度:
导读JS幻想读取二进制文件_javascript技巧:如果说让JavaScript读取站点上一文本文件,那不过是个再简单不了的事了;但若说要换成一个二进制的文件,并且是完全静态的读取,那似乎有点天方夜谭了。且不说浏览器内置的HTTP插件是否支持二进制数据流,就JavaScript其自身就毫无二进制的处理能力。聪明的读者

如果说让JavaScript读取站点上一文本文件,那不过是个再简单不了的事了;但若说要换成一个二进制的文件,并且是完全静态的读取,那似乎有点天方夜谭了。

且不说浏览器内置的HTTP插件是否支持二进制数据流,就JavaScript其自身就毫无二进制的处理能力。聪明的读者也许想说用VBScript就可以实现了。不错,因为VBScript,IE,ActiveX都是微软的产物,所以他们有着无缝的结合。IE的HTTP组件确实能够读取二进制数据,而且也只能够让VBScript读取。但对于其他浏览器,就束手无策了。

毕竟脚本的理念仅仅是用来处理一些简单的交互的,对于处理字节流之类的复杂问题完全不该是脚本的职责。不过作为一种探索,我们还是可以挖掘下其中的乐趣。当然,首先要明确的是,对于二进制的读取JS确实是无能为力的,不过我们可以来模拟,以达到相同的效果,下面就跟着我来吧。

比如现在想要做个推箱子的小游戏,共200关。这时唯一值得考虑到事就出现了:把这200关地图数据保存在何处?如果直接硬塞在一个脚本文件里似乎显得太大,或者单独保存在一个文件里,但是用什么格式。。不过对于推箱子游戏来说简单的文本格式也够了,但对于一些地图较复杂的也许就会使用BASE64编码,然后由客户端的HTTP组件下载下来解码使用。BASE64编码在JS中还是很常用的,毕竟它不受任何的环境限制,能够处理字符串就行。

既然有个BASE64,那为什么就不能有BASE128,BASE256了呢?如果能实现“BASE256”,岂不就是二进制字节流了?如果真可以如此,那这种方法早就流传开了,还留着那么多的BASE64做什么:)毕竟这是字符串,而不叫字节串,那肯定是有区别的。不妨把一个二进制的文件,当作文本文件读取回来试试,很快你就会发现超过一旦文件中出现127(0x7F)以上的字符,马上就出错了;如果存在个0x00字节的话,后面的内容都会荡然无存,这意味着256个字符中能够利用的还不到一半。

然而,可别忘了这个测试使用的仅仅是最基本的ASCII编码,对于功能强大的XMLHTTP支持的也绝不仅限于如此,那么就试试Unicode字符会怎样。在记事本随便输几个字符,保存为Unicode格式的文本文件。这时用XMLHTTP读取,显示出来的与记事本里的一模一样,但是再用16进制编辑器打开此文件时,就大不相同了。在文件的开头出现了FF FE两字节,后面的每个内容都是由一个0隔开。毕竟这是16位的Unicode字符,除了基本的ASCII外,还要保存各国的文字。例如一个中文就占用了2个字节,而英文数字仍然占用2字节,只是高位由0填充罢了(注意高位字节是在低位字节后面的)。
XMLHTTP能够成功显示出来就说明Unicode还是支持的。现在试着修改文件里面的数据,看看超过了那些范围才会出错。把数据修改成如下:FFFE 0001 0203 7F00 8000 8100 FF00 FFFF。用XMLHTTP测试,虽然显示的都是乱码,但并没出错,返回的字符串用charCodeAt(i)及toString(16)方法一试,原形毕露!几经测试,Unicode并不像ASCII那样有范围限制,但唯独一个例外:0x0000!
众所周知,0x00就是ASCII的结束标志。但到了Unicode的世界里一切都是16位的,因此字符尾也成了0x0000。到了这里似乎有点遗憾,但接着的目标很明确:如果能够去掉文件中的0x0000,并在之前加上0xFEFF,就能够让JavaScript读取了。

去掉以及恢复,不妨就称他编码与解码吧。编码的方法就见智见仁了,最简单的办法就是记录下每个0x0000的位置,然后除去;在客户端按照记录的位置再复原回去。虽然简单,但也别忘了,0x00在二进制文件中是相当多的,即便是0x0000也是。这样光是记录他们的内容就有很多,显然不是很好。既然说到要记录,为何一定要记录0x0000的位置?反过来想,我们应该记录这个文件中出现次数最少的字符,以及它的位置,然后把0x0000的地方替换成这个字符;解码的时候一旦出现这个字符,但当前位置又不在记录中,就可以确定这就是个0x0000。事实上,在64K以下的文件中肯定有个字符根本就不会出现的(为什么仔细考虑下就明白),即使是在64K以上,还是有非常多的文件不存在某个字符的。毕竟一个Unicode字符有65536之多,很少有文件会把他们全都用上,除非是个冗余极小的压缩文件,但也不会很多。

到此,编码解码思路已明了,剩下自然是实现他们。
刚才提到了源文件中出现最少(甚至是没有)的Unicode字符,不妨就称作key
首先来定义新生成的二进制文件头格式:

代码如下:

00 01 0xFEFF。 Unicode文件头,这是必须的 
02 03 Key值。 为了不让0x0000成为Key,在寻找的过程中忽略0x0000 
03 04 Key出现的次数+1。 +1是为了避免该位置出现0x0000,后面的也都一样 
05 06 
07 08 第1个Key的位置 用4字节保存每个Key的位置。 
09 0A 
0B 0C 。。 
0D 0E 
0F 10 第n个Key的位置 
11 12 文件数据内容。。

编码程序虽不复杂,不过也不是几句就能搞定的,为方面这里给个ASP版本的(运行效率非常低,不过处理小文件还是很快的)。JavaScript的解码程序倒是非常的简短,放在Demo.html里一起贴出了。
JSBin.asp:

代码如下:

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%> 
<%Option Explicit%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>JSBin</title> 
</head> 
<body> 
<% 
'================================================== 
' 类: Stream 
'================================================== 
Const adTypeBinary = 1 
Const adLongVarBinary = 205 
Const adSaveCreateOverWrite = 2 
Class Stream 
 Dim bytBuffer 
 Dim lngSize 
 Dim lngOffset 
 '================================================== 
 ' 方法: Load 
 ' 说明: 从文件载入数据流 
 '================================================== 
 Public Function Load(Path) 
 Dim objADOStream 
 Dim binData 
 Dim i 
 Set objADOStream = Server.CreateObject("ADODB.Stream") 
 With objADOStream 
 .Type = adTypeBinary 
 .Open 
 End With 
 With objADOStream 
 .LoadFromFile Path 
 binData = .Read 
 .Close 
 End With 
 Set objADOStream = Nothing 
 lngSize = Ubound(binData) 
 ReDim bytBuffer(lngSize) 
 lngOffset = 0 
 ' 
 ' 读取数据 
 ' 
 For i = 0 To lngSize 
 bytBuffer(i) = AscB(MidB(binData, i + 1, 1)) 
 Next 
 lngSize = lngSize + 1 
 End Function 
 '================================================== 
 ' 方法: Save 
 '================================================== 
 Public Function Save(Path) 
 Dim objADOStream 
 Dim objRS 
 Dim i 
 Dim binData 
 Set objADOStream = Server.CreateObject("ADODB.Stream") 
 Set objRS = Server.CreateObject("ADODB.Recordset") 
 ' 
 ' ASP处理二进制只能如此 
 ' 
 For i = 0 To lngSize - 1 
 binData = binData & ChrB(bytBuffer(i)) 
 Next 
 With objRS 
 .Fields.Append "t", adLongVarBinary, lngSize 
 .Open 
 .AddNew 
 .Fields("t").AppendChunk binData 
 .Update 
 binData = .Fields("t").GetChunk(lngSize) 
 End With 
 With objADOStream 
 .Type = adTypeBinary 
 .Open 
 .Write binData 
 .SaveToFile Path, adSaveCreateOverWrite 
 .Close 
 End With 
 Set objADOStream = Nothing 
 Set objRS = Nothing 
 End Function 
 '================================================== 
 ' 方法: Seek 
 ' 说明: 定位字节流当前位置 
 '================================================== 
 Public Function Seek(pos) 
 lngOffset = pos 
 End Function 
 '================================================== 
 ' 方法: Read 
 '================================================== 
 Public Function ReadByte() 
 ReadByte = bytBuffer(lngOffset) 
 lngOffset = lngOffset + 1 
 End Function 
 '================================================== 
 ' 方法: WriteUInt 
 '================================================== 
 Public Function WriteUInt(Code) 
 bytBuffer(lngOffset) = CByte(Code Mod 256) 
 bytBuffer(lngOffset + 1) = CByte(Code \ 256) 
 lngOffset = lngOffset + 2 
 End Function 
 '================================================== 
 ' 属性: Size 
 '================================================== 
 Public Property Get Size() 
 Size = lngSize 
 End Property 
 Public Property Let Size(value) 
 lngSize = value 
 ReDim Preserve bytBuffer(lngSize - 1) 
 End Property 
End Class 
'================================================== 
' 类: Vector 
'================================================== 
Const DEFAULT_SIZE = 20 
Const NUM_INC = 50 
Class Vector 
 Dim arrContainer() 
 Dim lngSize 
 Dim lngCount 
 '================================================== 
 ' 过程: 类构造 
 '================================================== 
 Private Sub Class_Initialize() 
 lngCount = 0 
 lngSize = DEFAULT_SIZE 
 ReDim arrContainer(DEFAULT_SIZE - 1) 
 End Sub 
 Private Sub Class_Terminate() 
 End Sub 
 '================================================== 
 ' 属性: Add 
 '================================================== 
 Public Function Add(value) 
 If lngCount = lngSize Then 
 lngSize = lngSize + NUM_INC 
 ReDim Preserve arrContainer(lngSize) 
 End If 
 arrContainer(lngCount) = value 
 lngCount = lngCount + 1 
 End Function 
 '================================================== 
 ' 属性: Item 
 '================================================== 
 Public Property Get Item(id) 
 Item = arrContainer(id) 
 End Property 
 '================================================== 
 ' 属性: Count 
 '================================================== 
 Public Property Get Count() 
 Count = lngCount 
 End Property 
End Class 
'================================================== 
' 函数: JSBin 
' 说明: 将制定的文件转换为JS兼容的二进制文件 
' EtherDream 08/06/10 
'================================================== 
Function JSBin(FileIn, FileOut) 
 Const USHRT_MAX = 65536 
 Dim objStream 
 Dim lngFileLen 
 Dim lngSize 
 Dim intBuffer() 
 Dim Table(65535) 
 Dim intVal 
 Dim vctKey 
 Dim vctZero 
 Dim intKeyNum 
 Dim intKeyVal 
 Dim i 
 ' 
 ' 建立脚本字节流对象 
 ' 
 Set objStream = New Stream 
 Set vctKey = New Vector 
 Set vctZero = New Vector 
 ' 
 ' 载入文件 
 ' 
 objStream.Load FileIn 
 lngFileLen = objStream.Size 
 lngSize = (lngFileLen - 1) \ 2 
 ' 
 ' 将字节流转换为整型数组 
 ' 
 ReDim intBuffer(lngSize) 
 On Error Resume Next 
 With objStream 
 For i = 0 To lngSize 
 intVal = .ReadByte() 
 intVal = intVal + .ReadByte() * 256 
 intBuffer(i) = intVal 
 Next 
 End With 
 On Error Goto 0 
 ' 
 ' 计数器清零 
 ' 
 Table(0) = USHRT_MAX 
 For i = 1 To USHRT_MAX - 1 
 Table(i) = 0 
 Next 
' 
' 统计每个Unicode字符出现的次数(\0\0除外) 
' 
 With vctZero 
 For i = 0 To lngSize 
 intVal = intBuffer(i) 
 If intVal = 0 Then 
 .Add i 
 Else 
 Table(intVal) = Table(intVal) + 1 
 End If 
 Next 
 End With 
' 
' 寻找出现次数最少的Unicode 
' 
 intKeyNum = USHRT_MAX 
 For i = 0 To USHRT_MAX - 1 
 intVal = Table(i) 
 If intVal < intKeyNum Then 
 intKeyNum = intVal 
 intKeyVal = i 
 End If 
' 
' 发现从未出现过的字符直接完成 
' 
 If intKeyNum = 0 Then 
 Exit For 
 End If 
 Next 
 ' 
 ' 寻找并记录整型数组中所有intKeyVal的位置 
 ' 
 If intKeyNum > 0 Then 
 With vctKey 
 For i = 0 To lngSize 
 If intBuffer(i) = intKeyVal Then 
 .Add i 
 End If 
 Next 
 End With 
 End If 
 ' 
 ' 将整型数组中的0替换为intKeyVal 
 ' 
 With vctZero 
 For i = 0 To .Count - 1 
 intBuffer(.Item(i)) = intKeyVal 
 Next 
 End With 
 Dim pos 
 ' 
 ' 生成目标文件 
 ' 
 With objStream 
 .Size = 6 + intKeyNum * 4 + (lngSize + 1) * 2 
 .Seek 0 
 .WriteUInt 65279 'Unicode文件头 0xFEFF 
 .WriteUInt intKeyVal '出现最少的Unicode值 (0已排除) 
 .WriteUInt intKeyNum + 1 '出现最少的Unicode次数 (避免0) 
 For i = 0 To intKeyNum - 1 '记录每个最少值的出现位置 
 pos = vctKey.Item(i) 
 .WriteUInt (pos MOD 65535) + 1 '(避免0) 
 .WriteUInt (pos \ 65535) + 1 '(避免0) 
 Next 
 For i = 0 To lngSize 
 .WriteUInt intBuffer(i) 
 Next 
 ' 
 ' 保存数据至文件 
 ' 
 .Save FileOut 
 Response.Write "转换完成!<br>保存至 " & FileOut & "<br>源文件: " & lngFileLen & "字节.<br>转换后: " & .Size & "字节." 
 End With 
 Set objStream = Nothing 
 Set vctZero = Nothing 
 Set vctKey = Nothing 
End Function 
Sub Main() 
 Dim strFile 
 Dim strFileIn 
 Dim strFileOut 
 strFile = Request.QueryString("path") 
 strFileIn = Server.MapPath(strFile) 
 strFileOut = Server.MapPath(strFile & ".txt") 
 JSBin strFileIn, strFileOut 
End Sub 
Main 
%> 
</body> 
</html>

使用时加上path参数即可对指定的文件编码,比如JSBin.asp?path=123.rar,就会对123.rar编码,并生成123.rar.txt的文件.
客户端的可以在我的空间上预览:
http://www.gxlcms.com/
可以在里面输入 JSBin.rar.txt,123.jpg.txt,jsmin.exe.txt即对相应的编码文件加载,所显示的内容与编码前的文件一模一样.对一个二进制的文件仅仅做了几个字节的修改,就能让JavaScript读取,不是很有趣吗?

文档

JS幻想读取二进制文件_javascript技巧

JS幻想读取二进制文件_javascript技巧:如果说让JavaScript读取站点上一文本文件,那不过是个再简单不了的事了;但若说要换成一个二进制的文件,并且是完全静态的读取,那似乎有点天方夜谭了。且不说浏览器内置的HTTP插件是否支持二进制数据流,就JavaScript其自身就毫无二进制的处理能力。聪明的读者
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top