从JavaScript字符串中读取字节

32 javascript

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

问题是JavaScript中的字符串是UTF-16(而不是ASCII),并且charCodeAt通常返回高于256的值.

Mozilla的参考指出,"前128个Unicode码点是ASCII字符编码的直接匹配".(那么ASCII值> 128?).

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

Bor*_*gar 37

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

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]
Run Code Online (Sandbox Code Playgroud)

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

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"
Run Code Online (Sandbox Code Playgroud)

  • 您输出的编码甚至没有明确定义.在大多数情况下,您将无法在此虚构编码和字符串之间进行往返. (2认同)

Moo*_*oos 15

Borgar的回答似乎是正确的.

只想澄清一点.Javascript将按位操作视为'32位有符号的int,其中最后一个(最左侧)位是符号位.也就是说,

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

getIntAt([0x80,0,0,0],0).toString(16)  // "-80000000"
Run Code Online (Sandbox Code Playgroud)

但是,对于八位位组数据处理(例如,网络流等),通常需要'unsigned int'表示.这可以通过添加一个'>>> 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"
Run Code Online (Sandbox Code Playgroud)


Kad*_*adm 14

有两种方法可以将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)); // ??!
Run Code Online (Sandbox Code Playgroud)


小智 9

虽然@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; 
}
Run Code Online (Sandbox Code Playgroud)


cod*_*joe 5

我暂时假设您的目标是从字符串中读取任意字节。我的第一个建议是使您的字符串表示形式成为二进制数据的十六进制表示形式。

您可以使用十六进制转换为数字来读取值:

var BITS_PER_BYTE = 8;

function readBytes(hexString, numBytes) {
    return Number( parseInt( hexString.substr(0, numBytes * (BITS_PER_BYTE/4) ),16 ) );
}

function removeBytes(hexString, numBytes) {
    return hexString.substr( numBytes * (BITS_PER_BYTE/BITS_PER_CHAR) );
}
Run Code Online (Sandbox Code Playgroud)

然后可以使用这些函数来读取您想要的任何内容:

var hex = '4ef2c3382fd';
alert( 'We had: ' + hex );

var intVal = readBytes(hex,2);
alert( 'Two bytes: ' + intVal.toString(2) );

hex = removeBytes(hex,2);
alert( 'Now we have: ' + hex );
Run Code Online (Sandbox Code Playgroud)

然后您可以根据需要解释该字节字符串。

希望这可以帮助!干杯!


Geo*_*tis 5

Borga的解决方案非常有效.如果你想要一个更具体的实现,你可能想看看vjeux中的BinaryReader类(对于记录,它基于Jonas Raoni Soares Silva二进制解析器类).