用JavaScript读写二进制文件的另一种方法

标签: , , , , , ,

曾经写过一篇《用JavaScript读写二进制文件》,其实严格的说是JScript,但是很多不明真相的同学根本不区分。内容摘自CodeProject《Reading and Writing Binary Files Using JScript》一文。最近要用JScript处理一些二进制的*.torrent文件,重新看了一遍原文,发现有人在回复中提供了一种更简洁的方法

a shorter and quicker way [modified]》,英文好的同学自己看原文,我为英文不好的同学简单翻译一下,虽然我英文也很烂。


我一直在寻找怎样在WSH中利用JScript处理二进制文件,这是我找到的最好的方法但是我很快就意识到你可以使用1252代码页(Demon注:详见《ISO-8859-1和Windows-1252的区别》),这不需要庞大的映射,并且经过一番睡觉之后(Demon注:原文是after sleeping on it,实在不知道怎么翻译了o(╯□╰)o),我意识到把读到的字符一个一个映射是非常繁琐和缓慢的。于是我原封不动的保留读取的字符串,但把它封装成一个拥有映射方法的字符串对象,像这样:

// When we read a binary stream as ISO 8859-1 (Latin 1), we should get a string
// 当我们用ISO 8859-1 (Latin 1)编码读取二进制流的时候,我们将得到一个字符串,
// where each charCodeAt value matches the byte from the stream.  Unfortunately
// 每个字符的charCodeAt值都对应二进制流的一个字节。遗憾的是
// Windows won't give you Latin 1 -- when you ask for it, you get code page
// Windows并不给你Latin 1 —— 当你请求它的时候,你得到的是代码页
// 1252, which has extra characters stuck in for byte values from 128 to 159.
// 1252,它含有额外的字符,会影响从128到159的字节的值
// These two strings allow us to translate between the bogus Windows characters
// 这两个字符串允许我们完成虚假的Windows字符和原始字节值之间的转换
// and the original byte values.
var bogusWindows1252chars = "\u20AC\u201A\u0192\u201E\u2026\u2020\u2021" +
"\u02C6\u2030\u0160\u2039\u0152\u017D" +
"\u2018\u2019\u201C\u201D\u2022\u2013\u2014" +
"\u02DC\u2122\u0161\u203A\u0153\u017E\u0178";
// No translation is necessary for characters 0x81, 0x8D, 0x8F, 0x90, or 0x9D.
// 0x81, 0x8D, 0x8F, 0x90, or 0x9D的字符不需要转换
var correctLatin1chars    = "\u0080\u0082\u0083\u0084\u0085\u0086\u0087" +
"\u0088\u0089\u008A\u008B\u008C\u008E" +
"\u0091\u0092\u0093\u0094\u0095\u0096\u0097" +
"\u0098\u0099\u009A\u009B\u009C\u009E\u009F";


// This turns a string read as codepage 1252 into a boxed string with a
// 返回一个以1252代码页读取的字符串,并封装了byteAt方法
// byteAt method.  We also modify the slice method to return a similar object.
// 我们也修改了slice方法使之返回一个相同的对象
function binaryString(str)
{
    var r = str ? new String(str) : new String();
    // always return an object with a .length
    // 总是返回一个含有 .length属性的对象
    r.byteAt = function(index)
    {
        // translate character back to originating Windows-1252 byte value
        // 把Windows-1252字节转换成原始字节值
        if (this.charCodeAt(index) <= 255)
            return this.charCodeAt(index);
        var p = bogusWindows1252chars.indexOf(this.charAt(index));
        return correctLatin1Chars.charCodeAt(p);
    };
    r.slice  = function(start, end)
    {
        return binaryString(this.substring(start, end));
    };
    return r;
}

// Does reverse translation from bytes back to Windows-1252 characters.  You can
// 完成相反的从原始值向Windows-1252字符的转换
// build up a string to write back to disk by concatenating a bunch of these.
// 你可以建立一个写回硬盘的字符串,以连接的方法
function fromByte(num)
{
    var c = String.fromCharCode(num);
    var p = correctLatin1chars.indexOf(c);
    return p >= 0 ? bogusWindows1252chars.charAt(p) : c;
}


// Reads bytes from a file, returning them as a binaryString.
// 读取文件的字节,以二进制字符串的形式返回它们
function binaryReadFile(path, maxLength)
{
    var binstream = new ActiveXObject("ADODB.Stream");
    binstream.Type = 2 /*adTypeText 文本模式 */;
    binstream.Charset = "iso-8859-1";   // actually Windows codepage 1252 其实是Windows-1252
    binstream.Open();
    binstream.LoadFromFile(path);
    return binaryString(binstream.ReadText(maxLength));
}

我仅仅实现二进制读取,而没有实现二进制写入,但是结合上下文,这是很简单的。你可能想把 fromByte 函数拓展成为一个能够转换较大的块的方法,比如像产生大端序或者 UTF-8 的字符串的方法,或者任何你想要的格式,这样你就不需要每次都一个一个地连接字节。

这样比原来短了很多,不是吗?也许还会更快,至少你不用多次处理同一字节。在我使用的某些地方,它确实比较快,比如读取MP3文件并提取一些它们的ID3标签。在这个情况下,你只需要用indexOf来寻找标签,这样只有很少的字符需要转换成字节。

如果需要更大的读写,可以把映射缓存到数组中。这样 byteAt 和 fromByte 就会变得很短并且很快。你仅仅需要初始化一个循环来操作它们,从bogusWindows1252chars到correctLatin1chars字符串。


翻译得很烂,有个别语句我自己都没看懂。在这里纠正一下原作者的错误, ADODB.Stream很BUG,当你用iso-8859-1字符集读取文本的时候,跟上面说的一样,Windows不会给你iso-8859-1(无视标准是微软的一贯作风),而是用Windows-1252字符集(两者的区别详见《ISO-8859-1和Windows-1252的区别》)。所以得到的字符串的charCodeAt值与原二进制流并不一一对应,需要将Windows-1252额外的字符做一个映射。但是,如果你用iso-8859-1写入文本,这时的的确确是iso-8859-1,Windows不会再当成Windows-1252。所以,读取的字符串并不需要再次转回Windows-1252,原作者写错了。

随机文章:

  1. PuTTY SSH Tunnel设置
  2. Asc/AscB/AscW和Chr/ChrB/ChrW函数之间的区别
  3. md5sum for windows
  4. VBS的一个BUG
  5. PHP & JavaScript: UTF-16 to UTF-8

一条评论 发表在“用JavaScript读写二进制文件的另一种方法”上

  1. 纠正说道:

    “会影响从128到159的字节的值”翻译错了 应该 是 “二进制 值 为 128 到 159的时候 都会被 加上 控制 符”

留下回复