Loading

Javascript如何处理字节,16进制字符串与数字(数组)相互转换,16进制转有符号/无符号数字

背景介绍

在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
posted @ 2022-11-13 23:11  sq800  阅读(3164)  评论(0编辑  收藏  举报