![Board logo](images/default/logo.gif)
标题: [其他] BAT+JScript+VBScript混排,实现批处理读取二进制文件结构体 [打印本页]
作者: amwfjhh 时间: 2015-5-19 11:24 标题: BAT+JScript+VBScript混排,实现批处理读取二进制文件结构体
本帖最后由 amwfjhh 于 2015-5-19 11:51 编辑
接到一个任务,需要将一些备份资源中的数据导出,其中有许多图片资源与文本描述,而这些图片的编号及其对应的说明则在一个后缀为.db的数据库中,这个数据库有一个专门的应用程序来打开,本想写个脚本来批量完成这事,可是问题来了:如何批量读取数据库里的东西呢?这数据库既不是mysql,也不是sqlserver,更不是PostgreSQL或oracle之类的已知数据库,想调用数据库引擎来读取内容也没法,于是尝试二进制方式读取,用ultraedit打开.db文件,查看它的二进制代码规律,万幸能在其中找到一点蛛丝马迹:
这是它提供的数据库读取程序读出来的内容:
[attach]8713[/attach]
这是ultraedit打开的二进制内容:
[attach]8714[/attach]
可以看到其中规律还是很明显的,数据主体部分应该是用到了一个数据结构体,从其数据库打开程序所看到的关键信息都能在其中找到对应的位置,那我们只需要按照规律读取出来就行了。- 0000080ah: 80 01 D1 AD BB B7 CF B5 CD B3 D5 FD B3 A3 58 CF ; .循环系统正常X?
- 0000081ah: DF B1 ED CF D6 00 00 00 00 00 00 00 00 00 00 00 ; 弑硐?..........
- 0000082ah: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
- 0000083ah: 00 00 00 00 80 03 ; .....
复制代码
这是其中一个结构体的信息,可以看到之后的数字为该条目的编号或者其所拥有的章节数,第一个后的文本部分即为数据表格内容,读取流数据,我们可以用adodb.stream来进行,基于在一个脚本内写完所有内容的要求,我们采用BJ混排的方式,将jscript和bat脚本写到同一个文件里,利用cscript的-e参数可将批处理作为jscript脚本来调用,达到自身既是批处理,也是jscript脚本的目的。但这里面有一个麻烦就是,文本信息是用的中文(或说文本)来存储的,但是编号和拥有章节数这两个数据却不是以文本方式存储的,可以直接将其值以字节的方式存储在二进制文件里面,这对于c或c++之类的语言来说完全没问题,但是对于adodb.stream这个组件来说,它要么吐出字符串,要么吐出字节流,而字节流,在jscript中又无法直接处理,且其获取字符串编码的方式charCodeAt(n)则是直接获取的unicode代码,显示对于直接的字节流支持不是很好,那我们要怎么把这个数据取出来呢,VBScript!虽然有了BJ混排后我极力想将其抛弃,但不得不说,在这里,对于字节流的支持,其leftb,ascb等方法却是救命稻草,能完美解决我的问题,于是方案就出来了,bat调用jscript,创建adodb.stream组件,对文件进行读取,在读取编号时,将文件以流方式读取,再创建一个临时的vbs脚本,用于调用ascb方法,对流编码进行读取,代码如下:- @if (0)==(0) echo off
- setlocal enabledelayedexpansion
-
- REM set /p "strFile=Input binary file:"
- REM set /p "strOffset=Input offset:"
- REM set /p "strLen=Input length of data:"
-
- set "strFile=a.db"
- set "strOffset=2059"
- set "strLen=58"
-
- for /l %%i in (0 1 6) do (
- set /a offsetNum=!strOffset!+%%i*!strLen!
- set /a offsetTxt=!offsetNum!+3
- set /a offsetNum1=!offsetNum!+52
- echo,!offsetNum! !offsetTxt! !offsetNum1!
- cscript -nologo -e:jscript %~s0 GetBinNumber "!strFile!" !offsetNum!
- for /f "delims=" %%b in ('cscript -nologo -e:vbscript "$"') do (set "strIndex=%%b") & del /f "$"
- for /f "delims=" %%a in ('cscript -nologo -e:jscript %~s0 GetBinText "!strFile!" !offsetTxt! !strLen!') do (set "strLine=%%a")
- cscript -nologo -e:jscript %~s0 GetBinNumber "!strFile!" !offsetNum1!
- for /f "delims=" %%b in ('cscript -nologo -e:vbscript "$"') do (set "strIndex1=%%b") & del /f "$"
-
- echo,第 !strIndex! 章, !strLine!, 拥有节数 !strIndex1!
- REM pause>nul
- )
-
- pause
- GOTO :EOF
- @end
-
- var fun = WScript.arguments(0);
-
- switch (fun) {
- case "GetBinText":
- CheckArgs(4);
- var fil = WScript.arguments(1);
- var offset = parseInt(WScript.arguments(2));
- var len = parseInt(WScript.arguments(3));
- GetBinText(fil, offset, len);
- break;
-
- case "GetBinNumber":
- CheckArgs(3);
- var fil = WScript.arguments(1);
- var offset = parseInt(WScript.arguments(2));
- GetBinNumber(fil, offset);
- break;
-
- default:break;
- }
-
- function CheckArgs(num){
- if (WScript.arguments.length < num){
- WScript.echo("参数不齐");
- WScript.quit();
- }
- }
-
- function GetBinText(fil, offset, len){
- var stream;
- var strContent;
-
- stream = new ActiveXObject("adodb.stream");
- stream.open();
- stream.loadFromFile(fil);
- stream.type = 2;
- stream.charset = "gb2312";
- stream.position = offset;
- strContent = stream.readText();
- stream.close();
-
- WScript.echo(strContent);
- }
-
- function GetBinNumber(fil, offset){
- var stream, stream1;
-
- stream = new ActiveXObject("adodb.stream");
- stream.open();
- stream.type = 1;
- stream.loadFromFile(fil);
- stream.position = offset;
-
- //输出序号
- var stream1 = new ActiveXObject("adodb.stream");
- stream1.mode = 3;
- stream1.open();
- stream1.type = 2;
- stream1.charset = "gb2312"
- stream1.writeText("WScript.echo ascb(leftb(\"");
- stream1.position = 0;
- stream1.type = 1;
- stream1.position = 25;
- stream1.write(stream.read(1));
- stream1.position = 0;
- stream1.type = 2;
- stream1.position = 26;
- stream1.writeText("\", 1))");
- stream1.saveToFile("$", 2);
- stream1.close();
-
- stream.close();
- }
复制代码
以下为代码运行效果,读出内容与数据无差异
[attach]8715[/attach]
[attach]8716[/attach]
作者: CrLf 时间: 2015-5-19 17:40
本帖最后由 CrLf 于 2015-5-19 17:48 编辑
可以不用临时文件,该方案可以模拟 WSH:http://www.bathome.net/thread-34109-1-1.html
其实这种明显是结构体的数据库,用 c 语言可能更合适:- //tcc 编译后,以 程序名.exe 数据库名.db 查看数据库
-
- #include <stdio.h>
-
- typedef unsigned char BYTE;
-
- struct data
- {
- BYTE head1[3];
- BYTE index;
- BYTE head1[2];
- char text[50];
- BYTE head1[1];
- BYTE section;
- };
-
- main(int argc, char **argv){
- int i;
- FILE *fp;
-
- struct data db;
-
- for(i=1;i<argc;i++){
- fp = fopen(argv[i],"rb");
- fseek(fp,22,SEEK_SET);
- while(!feof(fp)){
- if(!fread(&db,sizeof(db),1,fp))break;
-
- printf("第 %d 章,%s,拥有节数 %d\n",db.index,db.text,db.section);
- }
- fclose(fp);
- }
- }
复制代码
之前碰到过另一种文件格式,只有数据头和数据尾标记的,而且编码混杂,c 做起来不方便,最后用 ado 配合 vbs 正则了...
作者: CrLf 时间: 2015-5-19 18:00
如果只需要 text 值的话,也可以暴力点,利用 more 将 \0 转为 \r\n,再用 sed 过滤- more bin.db | sed -r "s/^.{,3}//g;/^$/d" | more
复制代码
作者: amwfjhh 时间: 2015-5-20 01:27
回复 3# CrLf
c还要去编译一次,哪有批处理这样写出来就直接运行爽,而且这是任务其中一环,文本,数据都要得到,然后还需作其它相应处理,最终将零散的资源再以需要的方式重组。
more处理二进制文件还有你说的附加效果?试试去…
作者: amwfjhh 时间: 2015-5-20 09:06
回复 2# CrLf
拜读了下基于mshta的三种语言混排方案,新技能GET+1。![](images/smilies/default/lol.gif)
欢迎光临 批处理之家 (http://bathome.net./) |
Powered by Discuz! 7.2 |