用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. 用C語言實現PHP的base64_encode函數
  2. VB6拾遺:函數指針與CallWindowProc函數
  3. OpenTextFile與Unicode
  4. VBS實現UTF-8轉Unicode(UTF-16)
  5. 也談Windows記事本的BUG

2 條評論 發表在“用JavaScript讀寫二進制文件的另一種方法”上

  1. 糾正說道:

    “會影響從128到159的字節的值”翻譯錯了 應該 是 “二進制 值 為 128 到 159的時候 都會被 加上 控制 符”

  2. dk說道:

    謝謝, 終于解決了

留下回複