标题: 用JavaScript读写二进制文件的另一种方法
作者: Demon
链接: https://demon.tw/programming/javascript-binary-file-two.html
版权: 本博客的所有文章,都遵守“署名-非商业性使用-相同方式共享 2.5 中国大陆”协议条款。
曾经写过一篇《用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,原作者写错了。
赞赏微信赞赏支付宝赞赏
随机文章:
“会影响从128到159的字节的值”翻译错了 应该 是 “二进制 值 为 128 到 159的时候 都会被 加上 控制 符”
谢谢, 终于解决了