如何在JavaScript(Cscript.exe)中读取二进制文件?

5

我认为使用Scripting.FileSystemObject类无法读取二进制文件。我能用ADODB.Stream吗?

还有其他什么建议吗?

我想获得一个字节数组。

谢谢。

7个回答

6

还可以将文件作为二进制文件读取,然后使用VBScript将返回的对象(一个变体数组)转换为JavaScript字节数组。为此,您需要使用.wsf文件将VBScript和JavaScript混合在一起。您仍然需要ADODB.Stream。

<job id="Something">
  <script id="BFRHelper.vbs" language="VBScript">
    Public Function VbBinaryToArray(Binary)
        Dim i
        ReDim byteArray(LenB(Binary))
        For i = 1 To LenB(Binary)
            byteArray(i-1) = AscB(MidB(Binary, i, 1))
        Next
        VbBinaryToArray = byteArray
    End Function
  </script>

  <script language="JavaScript" id="BFR2.js">

    (function(){

        BinaryFileReader = {};

        var FileReadTypes = {
            adTypeBinary : 1,
            adTypeText   : 2
        };

        BinaryFileReader.ReadAllBytes = function(path)
        {
            var bs = WScript.CreateObject("ADODB.Stream");
            bs.Type = FileReadTypes.adTypeBinary;
            bs.Open();
            bs.LoadFromFile(path);
            var what = bs.Read;
            bs.Close();
            var array = VbBinaryToArray(what).toArray();
            // I find the length property is 1 higher than it ought to be
            var aL = array.length;
            array.length = aL -1;
            return array;
        };
    })();

    var content = BinaryFileReader.ReadAllBytes(path); 

  </script>
</job>

这是正确且完整的唯一解决方案。这就是正确的方向。 - Eugene Ryabtsev
如何实现相反的操作呢?也就是说,从字节数组转换为变体。 - GetFree

4

我在codeproject上找到了使用JScript读写二进制文件的文章,虽然不完全符合我的要求,但已经很接近了。

该方法使用ADODB.Stream来读取本地文件。通常情况下,使用Scripting.FileSystemObject无法以一般方式读取二进制文件,因为无法使用fso设置代码页。fso总是使用环境代码页,这意味着结果会因机器配置而异。而ADODB.Stream允许程序指定代码页。接下来需要将读入的“文本”映射为正常的十六进制字节。以上文章提供了这部分内容。

下面是我成功使用的结果。

// BinaryFileReader.js
// ------------------------------------------------------------------
//
// give the ability to read a binary file into an array of bytes,
// to Javascript.
//
// the mapping is based on code from:
//   http://www.codeproject.com/KB/scripting/Exsead7.aspx
//
// Created    : Fri May 28 05:20:31 2010
// Last-saved : <2010-May-28 06:01:34>
//
// ------------------------------------------------------------------

(function(){

    BinaryFileReader = {};

    var FileReadTypes = {
        adTypeBinary : 1,
        adTypeText   : 2
    };

    var backward = [];
    backward['C7']   = '80';
    backward['FC']   = '81';
    backward['E9']   = '82';
    backward['E2']   = '83';
    backward['E4']   = '84';
    backward['E0']   = '85';
    backward['E5']   = '86';
    backward['E7']   = '87';
    backward['EA']   = '88';
    backward['EB']   = '89';
    backward['E8']   = '8A';
    backward['EF']   = '8B';
    backward['EE']   = '8C';
    backward['EC']   = '8D';
    backward['C4']   = '8E';
    backward['C5']   = '8F';
    backward['C9']   = '90';
    backward['E6']   = '91';
    backward['C6']   = '92';
    backward['F4']   = '93';
    backward['F6']   = '94';
    backward['F2']   = '95';
    backward['FB']   = '96';
    backward['F9']   = '97';
    backward['FF']   = '98';
    backward['D6']   = '99';
    backward['DC']   = '9A';
    backward['A2']   = '9B';
    backward['A3']   = '9C';
    backward['A5']   = '9D';
    backward['20A7'] = '9E';
    backward['192']  = '9F';
    backward['E1']   = 'A0';
    backward['ED']   = 'A1';
    backward['F3']   = 'A2';
    backward['FA']   = 'A3';
    backward['F1']   = 'A4';
    backward['D1']   = 'A5';
    backward['AA']   = 'A6';
    backward['BA']   = 'A7';
    backward['BF']   = 'A8';
    backward['2310'] = 'A9';
    backward['AC']   = 'AA';
    backward['BD']   = 'AB';
    backward['BC']   = 'AC';
    backward['A1']   = 'AD';
    backward['AB']   = 'AE';
    backward['BB']   = 'AF';
    backward['2591'] = 'B0';
    backward['2592'] = 'B1';
    backward['2593'] = 'B2';
    backward['2502'] = 'B3';
    backward['2524'] = 'B4';
    backward['2561'] = 'B5';
    backward['2562'] = 'B6';
    backward['2556'] = 'B7';
    backward['2555'] = 'B8';
    backward['2563'] = 'B9';
    backward['2551'] = 'BA';
    backward['2557'] = 'BB';
    backward['255D'] = 'BC';
    backward['255C'] = 'BD';
    backward['255B'] = 'BE';
    backward['2510'] = 'BF';
    backward['2514'] = 'C0';
    backward['2534'] = 'C1';
    backward['252C'] = 'C2';
    backward['251C'] = 'C3';
    backward['2500'] = 'C4';
    backward['253C'] = 'C5';
    backward['255E'] = 'C6';
    backward['255F'] = 'C7';
    backward['255A'] = 'C8';
    backward['2554'] = 'C9';
    backward['2569'] = 'CA';
    backward['2566'] = 'CB';
    backward['2560'] = 'CC';
    backward['2550'] = 'CD';
    backward['256C'] = 'CE';
    backward['2567'] = 'CF';
    backward['2568'] = 'D0';
    backward['2564'] = 'D1';
    backward['2565'] = 'D2';
    backward['2559'] = 'D3';
    backward['2558'] = 'D4';
    backward['2552'] = 'D5';
    backward['2553'] = 'D6';
    backward['256B'] = 'D7';
    backward['256A'] = 'D8';
    backward['2518'] = 'D9';
    backward['250C'] = 'DA';
    backward['2588'] = 'DB';
    backward['2584'] = 'DC';
    backward['258C'] = 'DD';
    backward['2590'] = 'DE';
    backward['2580'] = 'DF';
    backward['3B1']  = 'E0';
    backward['DF']   = 'E1';
    backward['393']  = 'E2';
    backward['3C0']  = 'E3';
    backward['3A3']  = 'E4';
    backward['3C3']  = 'E5';
    backward['B5']   = 'E6';
    backward['3C4']  = 'E7';
    backward['3A6']  = 'E8';
    backward['398']  = 'E9';
    backward['3A9']  = 'EA';
    backward['3B4']  = 'EB';
    backward['221E'] = 'EC';
    backward['3C6']  = 'ED';
    backward['3B5']  = 'EE';
    backward['2229'] = 'EF';
    backward['2261'] = 'F0';
    backward['B1']   = 'F1';
    backward['2265'] = 'F2';
    backward['2264'] = 'F3';
    backward['2320'] = 'F4';
    backward['2321'] = 'F5';
    backward['F7']   = 'F6';
    backward['2248'] = 'F7';
    backward['B0']   = 'F8';
    backward['2219'] = 'F9';
    backward['B7']   = 'FA';
    backward['221A'] = 'FB';
    backward['207F'] = 'FC';
    backward['B2']   = 'FD';
    backward['25A0'] = 'FE';
    backward['A0']   = 'FF';

    var hD="0123456789ABCDEF";

    var d2h = function(d)
    {
        var h = hD.substr(d&15,1);
        while(d>15) {d>>>=4;h=hD.substr(d&15,1)+h;}
        return h;
    }

    var h2d = function(h)
    {
        return parseInt(h,16);
    }

    var toByteArray = function(inString) {
        var encArray = [];
        var sL = inString.length;
        for (var i=0;i<sL;i++) {
            var cc = inString.charCodeAt(i);
            if(cc>=128) {
                var h = backward[''+d2h(cc)];
                cc = h2d(h);
            }
            encArray.push(cc);
        }
        return encArray;
    }


    var _internalReadAll = function(path) {
        var bs = WScript.CreateObject("ADODB.Stream")
        bs.Type = FileReadTypes.adTypeText;
        bs.CharSet = '437';
        bs.Open();
        bs.LoadFromFile(path);
        var what = bs.ReadText;
        bs.Close();
        return what;
    }

    BinaryFileReader.ReadAllBytes = function(name)
    {
        var string = _internalReadAll(name);
        return toByteArray(string);
    }

})();

使用方法如下:
    var bytes = BinaryFileReader.ReadAllBytes(filename);

在其他语言环境中它是如何工作的?你的“backward”翻译似乎是预设的。是否有可能在运行时构建翻译? - Eugene Ryabtsev

2

如果您更喜欢使用纯jScript和COM对象,也许我的方法会有用。它可以从二进制数据创建一个jscript字符串。我更喜欢使用COM对象而不是代码页转换,因为速度更快。

//Reads a binary file, returns a string
function binaryFileToString(fileName) {
    var binStream = new ActiveXObject("ADODB.Stream");
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var size = (fs.getFile(fileName)).size;

    binStream.Type = 1; //adTypeBinary
    binStream.Open;

    binStream.loadFromFile(fileName);
    var binVariant = binStream.read();
    var adLongVarChar = 201;
    var RS = new ActiveXObject("ADODB.Recordset");

    RS.fields.append("mBinary", adLongVarChar, size);
    RS.open();
    RS.addNew();
    RS("mBinary").appendChunk(binVariant);
    RS.update();
    return RS("mBinary").value;
};

这似乎与FSO File.OpenAsTextStream(1).ReadAll() 的工作方式完全相同。 - Eugene Ryabtsev

0
Function ReadByteArray(strFileName)
Const adTypeBinary = 1
Dim bin
    Set bin = CreateObject("ADODB.Stream")
    bin.Type = adTypeBinary
    bin.Open
    bin.LoadFromFile strFileName
    ReadByteArray = bin.Read
End Function

来自http://www.ericphelps.com/q193998/index.htm


2
嗯,将其翻译为JavaScript并没有解决问题。问题在于从bin.Read获取的内容不是JavaScript字节数组,而是变量数组。要使用该代码,需要将变量数组映射到JS字节数组。请参阅我的代码https://dev59.com/3k7Sa4cB1Zd3GeqP0hMW#2928304。 - Cheeso

0
Cheeso的答案可行,可以得到一个数字数组。另一种选择是将第5行替换为:

a.push(ts.Read(1));

这将返回一个由单个字符组成的数组,而不是数字数组。在我的系统(Windows 7 64位)上,使用循环逐个写入字节太慢了,即使Cheeso的看似等效的读取函数速度也可以接受。使用Write一次写入数组会在字节之间添加逗号。但是,由单个字符组成的数组表示的字节可以通过该函数写入。

function WriteByte (File, Byte) {
    var Text = "";
    for (I = 0; I < Byte.length; I ++) Text += Byte [I];
    File.Write (Text);
}

在我的应用程序中,File 以 ASCII 格式打开,速度可以接受。

在我的系统中,JavaScript 存在一个 bug:如果将包含 null 字符的字符串传递给函数,在函数中,字符串的 .length 属性是正确的,但第一个 null 字符后的每个字符都是 null(与 null 字符不同)。然而,在此处引用的函数中,Text 中的 null 字符被正确地和迅速地处理。

通过字符代替数字来表示字节的另一个优点是,每个字节由两个字节而不是四个字节表示。当然,缺点是许多字节操作需要使用 .charCodeAt(0) 方法。


0

我发现“WindowsInstaller.Installer”可以通过传递一定的参数逐个返回字符串字节。因此,您可以将其与“ADODB.Stream”结合使用来访问文件中的真实字节。

然后,您还可以使用“SAPI.SpFileStream”ActiveX对象将它们逐个或批量写入新文件(在俄语中:http://forum.script-coding.com/viewtopic.php?id=10092)。这取决于您的需要。

所有这些ActiveX对象都可以在纯JScript(扩展JavaScript)上正常工作,包括中文环境,预计它们已经默认内置在XP或2000以后的每个Windows操作系统中。

以下是一个示例(您需要将其保存为带有“js”扩展名的文本文件或使用HTA代替):

var doc = GetObject('\\', 'htmlfile');
doc.write("<xml><_ xmlns:dt='urn:schemas-microsoft-com:datatypes'><_ dt:dt='bin.hex'/></_></xml>");
var xml = doc.documentElement.firstChild.children[1];
xml.preserveWhiteSpace = 1;

var hex = xml.firstChild.childNodes[0],

bytesToHex = function(bytes)          //convert true bytes to hex string
{
    hex.nodeTypedValue = bytes;
    return hex.text;
},

hexToBytes = function(hString)        //convert hex string to true bytes
{
    hex.text = hString;
    return hex.nodeTypedValue;
},

source = new ActiveXObject('WindowsInstaller.Installer').CreateRecord(1),     //source file stream
memory = new ActiveXObject('ADODB.Stream'),                                   //memory stream
output = new ActiveXObject('SAPI.SpFileStream'),                              //output file stream

sourceRead = function(bytesCount)     //read true bytes from source file stream starting from its previous position
{
    memory.Position = 0;
    memory.Type = 2;                                            //adTypeText = 2
    memory.Charset = 'Unicode';
    memory.WriteText(source.ReadStream(1, bytesCount, 3));      //msiReadStreamDirect = 3
    memory.SetEOS();
    memory.Position = 0;
    memory.Type = 1;                                            //adTypeBinary = 1
    memory.Position = 2;                                        //skip "BOM" header
    return memory.Read(bytesCount);
}

source.SetStream(1, 'C:\\Windows\\System32\\shell32.dll');      //open source file stream for reading

memory.Open();                                                  //open memory stream for reading and writing

output.Format.Type = 1;                                         //SAFTText = 1
output.Open('your output path goes here.bin', 3);               //create and open output file for writing (SSFMCreateForWrite = 3)

var arrayOfTrueBytes = [],
arrayOfHexBytes = [],
trueByte;
for(var i = 0; i < 7; i ++)                                     //fill arrays with first 7 bytes of source file stream one by one
{
    trueByte = sourceRead(1);
    arrayOfTrueBytes[i] = trueByte;
    arrayOfHexBytes[i] = bytesToHex(trueByte);
}

doc.parentWindow.alert(arrayOfHexBytes);        //show hex representation of true bytes in array

for(var i = 0; i < 7; i ++)
    output.Write(arrayOfTrueBytes[i]);          //write true bytes one by one from array to output file stream

output.Write(sourceRead(10));                   //write a bunch of next 10 true bytes of source file stream to output file stream
output.Write('\u3231\x20');                     //write string bytes to output file stream
output.Write(hexToBytes('68656c6c6f'));         //write true bytes converted from hex string to output file stream

我在我的 HTML 应用程序“资源提取器”中使用了这些方法。因此,如果您需要有关如何处理二进制数据的复杂示例,也可以在https://tastyscriptsforfree.wixsite.com/page/scripts上检查此应用程序。

十六进制转换示例基于http://forum.script-coding.com/viewtopic.php?id=6957(俄语)。 - Vladimir Samarets

-1

在 JScript 中读取文件的字节。

    function readAllBytes(path) {
        var fso = new ActiveXObject("Scripting.FileSystemObject"),
            ts = fso.OpenTextFile(path, 1), a = [];
        while (!ts.AtEndOfStream)
            a.push(ts.Read(1).charCodeAt(0));

        ts.Close();
        return a;
    }

如何在读取时读取二进制并转换为十六进制?使用您的代码,而不是使用ADODB.Stream。 - Cobaia
ActiveXObject已被弃用,仅适用于IE。即使在某些浏览器中工作,也不应该使用它。 - Pian0_M4n

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接