我需要编写一个将字节数组(0到255之间的整数)“打包”成字符串的函数。我还需要能够执行相反的操作,以便从打包入的字符串中获取字节数组。这需要尽快完成。看到JavaScript具有16位字符串,我每个字符打包了两个字节。这是我的代码和测试:

function pack(bytes) {
    var str = "";
    for(var i = 0; i < bytes.length; i += 2) {
        var char = bytes[i] << 8;
        if (bytes[i + 1])
            char |= bytes[i + 1];
        str += String.fromCharCode(char);
    }
    return str;
}

function unpack(str) {
    var bytes = [];
    for(var i = 0; i < str.length; i++) {
        var char = str.charCodeAt(i);
        bytes.push(char >>> 8);
        bytes.push(char & 0xFF);
    }
    return bytes;
}

var tests = [
    [],
    [126, 0],
    [0, 65],
    [12, 34, 56],
    [0, 50, 100, 150, 200, 250]
];

console.log("starting tests");
tests.forEach(function(v) {
    var p = pack(v);
    console.log(v, p, unpack(p));
});


输出的结果是:

starting tests
[] "" []
[126, 0] "縀" [126, 0]
[0, 65] "A" [0, 65]
[12, 34, 56] "ఢ㠀" [12, 34, 56, 0]
[0, 50, 100, 150, 200, 250] "2撖죺" [0, 50, 100, 150, 200, 250]


我有我想要反馈的一些信息:


这是我第一次使用按位运算符。请问这是应该怎么做的吗?
可以提高速度吗?
在编码然后解码具有奇数个数的数组时,你们能想到任何丢弃最后0个字节的方法吗?字节? (请参阅测试#4)


评论

关于3.,只需在打包数组的末尾添加一个前哨,指示是否应包含最后一个字节。

好主意。我将在开始指示长度处有2个字节,但是这会将长度限制为65535。使用您的方法,我可以得到更多。不是我需要它,但是打破障碍总是很高兴。

到底为什么要在客户端对字节进行编码?似乎应该在服务器上执行。

谁说那是客户端?

我已经使用了这段代码,将0值传递给它时似乎出现了问题,它们被转换为省略,从而使将32位浮点数打包为4个字符是不可靠的。有什么解决办法吗?

#1 楼

function pack(bytes) {
    var str = "";
// You could make it faster by reading bytes.length once.
    for(var i = 0; i < bytes.length; i += 2) {
// If you're using signed bytes, you probably need to mask here.
        var char = bytes[i] << 8;
// (undefined | 0) === 0 so you can save a test here by doing
//     var char = (bytes[i] << 8) | (bytes[i + 1] & 0xff);
        if (bytes[i + 1])
            char |= bytes[i + 1];
// Instead of using string += you could push char onto an array
// and take advantage of the fact that String.fromCharCode can
// take any number of arguments to do
//     String.fromCharCode.apply(null, chars);
        str += String.fromCharCode(char);
    }
    return str;
}

function unpack(str) {
    var bytes = [];
    for(var i = 0; i < str.length; i++) {
        var char = str.charCodeAt(i);
// You can combine both these calls into one,
//    bytes.push(char >>> 8, char & 0xff);
        bytes.push(char >>> 8);
        bytes.push(char & 0xFF);
    }
    return bytes;
}


所以要全部放在一起

function pack(bytes) {
    var chars = [];
    for(var i = 0, n = bytes.length; i < n;) {
        chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff));
    }
    return String.fromCharCode.apply(null, chars);
}

function unpack(str) {
    var bytes = [];
    for(var i = 0, n = str.length; i < n; i++) {
        var char = str.charCodeAt(i);
        bytes.push(char >>> 8, char & 0xFF);
    }
    return bytes;
}


评论


\ $ \ begingroup \ $
请注意,String.fromCharCode.apply(null,chars);如果chars的长度大于65536,将在使用JavaScriptCore(即Safari)的浏览器中引发“ RangeError:超出最大调用堆栈大小”异常,并将完全锁定在比该长度稍小的数组上。完整的解决方案要么将字符串直接推入数组,要么使用fromCharCode的分块版本。我怀疑前者在Safari中速度更快。看到bugs.webkit.org/show_bug.cgi?id=80797
\ $ \ endgroup \ $
–马修
2012年12月12日12:03