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