在JS的世界中,需要逐字节处理甚至逐位地处理数据的场景不多。但是在物联网领域,通常使用自定义的 通信协议 ,通信使用包含N个字节的 数据帧 来传输数据。一条简单的数据帧用16进制表示,就像下面的示例一样:

6810106878ACEFFF1232432168

通常一条普通的数据帧只有几百个字节,连1kb都不到,但是能传递很丰富的数据。之所以这样,是因为其根据约定好的通信协议进行通信,压缩了很多的信息。想要处理这样的数据,只要根据约定好的内部通信协议逐字节地分析数据帧,取出数据域长度、数据域、校验和等等,再进行下一步的处理。如果您对这些内容不太了解或不太关注,也没关系,这不是本篇的重点。本篇主要是介绍怎么样使用JS处理类似的数据。

1.把16进制字符串转成数字数组

这是处理数据帧的第一步,因为我们需要逐字节地处理数据帧,就要逐字节地把它转化成方便JS处理的数据类型,数字数组就是一个理想的数据类型。因为16进制下是每两个字符代表一个字节,因此我们只需要每次向后取出两个字符,把它转成对应的数字即可。

function hexToArr(str) {
    // hex字符串长度通常都是2的倍数,但为了放止意外,判断一下长度,不是2的倍数就在最前面补0
    if (str.length % 2) str = "0" + str
    let arr=[]
    for (let i = 0; i < str.length; i+=2) {
        let a=parseInt(str.slice(i,i+2),16)
        arr.push(a)
    return arr

示例:https://jsbin.com/qozaqevoya/edit?js,console

2.把数字数组转为16进制字符串

一个数字数组[0x68,0x10,0x68,0xa0,0xb0,0x16],转成16进制字符串结果为"681068A0B016",注意数字不应该大于0xFF,即255

function arrToHex(arr) {
    return arr.reduce((accu, item) => {
        let a = item.toString(16)
        if (a.length < 2) a = "0" + a
        a=a.toUpperCase()
        return accu += a
    }, "")

3.数字转为无符号16位的整数

不足两字节,补足两字节;溢出两字节,舍去溢出部分

* number转为uint16对应的hex字符串 * @param {number} num function numToUint16Hex(num) { let a = num.toString(16).toUpperCase(); if (a.length > 4) { return a.slice(a.length - 4); } else { return a.padStart(4, "0");

4.10进制数字转为16进制字符串

10进制转16进制字符串,若长度为奇数,则前面补0

* 10进制数字转为16进制字符串 * @param {number} arg * @returns function numToHex(arg) { try { let a = arg.toString(16).toUpperCase(); return a.length % 2 == 1 ? "0" + a : a; } catch (e) { console.warn("数字转16进制出错:", e);

5.16进制字符串转成有符号的整数

原理:对于负数,原数(unsigned)减去溢出值。如1110 1100(0xEC)为负数, 236-256 = -20;

* 16进制字符串转成有符号的整数 * @param {string} hex 16进制字符串 function hexToSignedInt(hex) { if (hex.length % 2 != 0) { hex = "0" + hex; let num = parseInt(hex, 16); let maxVal = Math.pow(2, (hex.length / 2) * 8); if (num > maxVal / 2 - 1) { //这里是判断正负,最高位为1,就代表正数 num = num - maxVal; return num;

6.16进制字符串转成有符号的整数

直接使用parseInt即可,无需特殊处理

let hex = 'FFFF'
let num = parseInt(hex, 16);	//65535

7.数字逐位处理

有时候有逐位处理的需求,例如,取一个数高四位,或第四位。有两种解决方法,一种是转成二进制字符串,一种是位运算。

转成二进制字符串,再使用字符串方法处理:

let a = 19
let b = a.toString(2) //'10011'
b = b.padStart(8 , '0') //'00010011'
//取出低四位
let c = b.slice(-4) //'0011'
//取出高四位
c = b.slice(0 , 4) //'0001'
//取出第0位
c = b[7] //'1'
let a = 19
//取出低四位
let b = a & 0b00001111  //3
//取出高四位
let b = (a & 0xF0) >> 4  //1
//取出第0位
b = a & 0x01  //1

如果要逐位设置一个数字,可以逐位地拼接成一个二进制字符串,再转成数字:

let a = '0001' + '0011'
let b = parseInt(a , 2) //19