从 JavaScript 字符串中读取字节

IT技术 javascript
2021-03-10 05:05:12

我有一个包含 JavaScript 二进制数据的字符串。例如,现在我想从中读取一个整数。所以我得到前 4 个字符,使用charCodeAt,做一些移位等来得到一个整数。

问题是 JavaScript 中的字符串是 UTF-16(而不是 ASCII)并且charCodeAt经常返回大于 256 的值。

Mozilla的参考指出,“前128个Unicode码点是ASCII字符编码的直接匹配”。(ASCII 值 > 128 怎么样?)。

如何将结果转换为charCodeAtASCII 值?或者是否有更好的方法将四个字符的字符串转换为 4 字节的整数?

6个回答

我相信你可以通过相对简单的位操作来做到这一点:

function stringToBytes ( str ) {
  var ch, st, re = [];
  for (var i = 0; i < str.length; i++ ) {
    ch = str.charCodeAt(i);  // get char 
    st = [];                 // set up "stack"
    do {
      st.push( ch & 0xFF );  // push byte to stack
      ch = ch >> 8;          // shift value down by 1 byte
    }  
    while ( ch );
    // add stack contents to result
    // done because chars have "wrong" endianness
    re = re.concat( st.reverse() );
  }
  // return an array of bytes
  return re;
}

stringToBytes( "A\u1242B\u4123C" );  // [65, 18, 66, 66, 65, 35, 67]

通过将字节数组作为内存读取并将其添加为更大的数字来总结输出应该是一件简单的事情:

function getIntAt ( arr, offs ) {
  return (arr[offs+0] << 24) +
         (arr[offs+1] << 16) +
         (arr[offs+2] << 8) +
          arr[offs+3];
}

function getWordAt ( arr, offs ) {
  return (arr[offs+0] << 8) +
          arr[offs+1];
}

'\\u' + getWordAt( stringToBytes( "A\u1242" ), 1 ).toString(16);  // "1242"
的确。只有当您知道它是索引 1 处的 2 字节字时,它才有用。而且您绝对无法从输出字节数组中知道这一点。
2021-05-08 05:05:12
您输出的编码甚至没有明确定义。在大多数情况下,您将无法在这种虚构的编码和字符串之间进行往返。
2021-05-12 05:05:12

Borgar的回答似乎是正确的。

只是想澄清一点。Javascript 将按位运算视为“32 位有符号整数”,其中最后(最左侧)位是符号位。IE,

getIntAt([0x7f,0,0,0],0).toString(16)  //  "7f000000"

getIntAt([0x80,0,0,0],0).toString(16)  // "-80000000"

然而,对于八位字节数据处理(例如,网络流等),通常需要“无符号整数”表示。这可以通过添加一个 '>>> 0'(零填充右移)运算符来完成,该运算符在内部告诉 Javascript 将其视为无符号。

function getUIntAt ( arr, offs ) {
  return (arr[offs+0] << 24) +
         (arr[offs+1] << 16) +
         (arr[offs+2] << 8) +
          arr[offs+3] >>> 0;
}

getUIntAt([0x80,0,0,0],0).toString(16)   // "80000000"
我们可以将有问题的 << 24 替换为 * Math.pow(2, 24),而不是依赖一个奇怪的技巧 (>>> 0)。这将被视为 64 位双精度值,而不是被视为 32 位无符号整数。不过这可能会慢一点。返回 (b3 * Math.pow(2, 24)) + (b2 << 16) + (b1 << 8) + b0; 感谢您的解决方案,因此我修补了 jDataView :) github.com/vjeux/jsDataView/commit/...
2021-05-03 05:05:12

有两种方法可以将 utf-8 字符串编码和解码为字节数组并返回。

var utf8 = {}

utf8.toByteArray = function(str) {
    var byteArray = [];
    for (var i = 0; i < str.length; i++)
        if (str.charCodeAt(i) <= 0x7F)
            byteArray.push(str.charCodeAt(i));
        else {
            var h = encodeURIComponent(str.charAt(i)).substr(1).split('%');
            for (var j = 0; j < h.length; j++)
                byteArray.push(parseInt(h[j], 16));
        }
    return byteArray;
};

utf8.parse = function(byteArray) {
    var str = '';
    for (var i = 0; i < byteArray.length; i++)
        str +=  byteArray[i] <= 0x7F?
                byteArray[i] === 0x25 ? "%25" : // %
                String.fromCharCode(byteArray[i]) :
                "%" + byteArray[i].toString(16).toUpperCase();
    return decodeURIComponent(str);
};

// sample
var str = "Да!";
var ba = utf8.toByteArray(str);
alert(ba);             // 208, 148, 208, 176, 33
alert(ba.length);      // 5
alert(utf8.parse(ba)); // Да!
我喜欢这个解决方案。我投了赞成票。我不明白你为什么不包括127并选择十六进制0x7F我正在使用它来检测 JavaScript 中的多字节字符串。例如。"Şerban".length != toByteArray("Şerban").length
2021-04-15 05:05:12

虽然@Borgar正确回答了问题,但他的解决方案非常缓慢。我花了一段时间来追踪它(我在一个更大的项目中的某个地方使用了他的功能),所以我想我会分享我的见解。

我最终得到了类似@Kadm 的东西它不是快了几个百分点,而是快了 500 倍(毫不夸张!)。我写了一个小基准,所以你可以自己看看:)

function stringToBytesFaster ( str ) { 
var ch, st, re = [], j=0;
for (var i = 0; i < str.length; i++ ) { 
    ch = str.charCodeAt(i);
    if(ch < 127)
    {
        re[j++] = ch & 0xFF;
    }
    else
    {
        st = [];    // clear stack
        do {
            st.push( ch & 0xFF );  // push byte to stack
            ch = ch >> 8;          // shift value down by 1 byte
        }
        while ( ch );
        // add stack contents to result
        // done because chars have "wrong" endianness
        st = st.reverse();
        for(var k=0;k<st.length; ++k)
            re[j++] = st[k];
    }
}   
// return an array of bytes
return re; 
}
Borgar 和 Kadm 提供了提供不同结果的不同解决方案。Borgar 代码的这个(真的快得多)版本返回与 Borgar 代码相同的结果。它不会返回与 Kadm 代码相同的结果,作者从未声称它会返回。Borgar 方法提取原始字节(以与十六进制编辑器或 xxd 相同的方式)。它不知道“代码点”或 unicode。该Kadm方法解构他们使用encodeURIComponent方法这unicode的识别,并输出结果不同-虽然我无法解释解释实际差异。
2021-04-22 05:05:12
中文字符似乎存在一些问题。代码点与编码不同。
2021-05-06 05:05:12

Borga 的解决方案非常有效。如果您想要更具体的实现,您可能需要查看vjeuxBinaryReader 类(对于记录,该类基于 Jonas Raoni Soares Silva二进制解析器类)。

谢谢你的链接,非常非常有用
2021-05-11 05:05:12