写点什么

[JS 入门到进阶] 手写解析 uin8 数组的工具:解析二进制字节,太快太方便了!

作者:HullQin
  • 2022 年 8 月 31 日
    广东
  • 本文字数:2650 字

    阅读完需:约 9 分钟

我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

背景

我常常需要处理二进制数据,每次面对一堆二进制数据,需要自己逐个字节转换为二进制、十六进制,去阅读和理解,去排查问题。


举个例子:有一个场景是我的《我做了个《联机桌游合集: UNO+斗地主+五子棋》无需下载,点开即玩!叫上朋友,即刻开局!不看广告,不做任务,享受「纯粹」的游戏!》,我使用 websocket 传输二进制数据,我使用 protocol buffer 作为序列化协议。所有变量的值都用二进制表示,变量名也被用数字表示,体积很小。(另一种常见的序列化协议是 JSON,它是基于文本的序列化协议,空间利用率低,所以常常需要压缩)


  • 使用二进制的好处:空间利用率高,节约带宽,提升速度。

  • 使用二进制的坏处:调试难度高,开发成本高。

诉求

二进制的数据,通常用 uin8 数组表示。8 个二进制位就表示一个 byte(十进制的值为 0-255,即十六进制的 00 至 FF)。我们调试二进制数据时,也常常看到诸如43 6c 6f 73表示的数据,这是十六进制,每 2 位十六进制,就代表了一个 byte。当然,也有时候使用十进制打印的。


但是十进制、十六进制,都不能满足诉求。


有时候我们需要以二进制表示,直观看清每一位的值。有时候我们需要把整体作为一个整数(例如整体作为 uint32),看看它的值。有时候我们需要把每位按照 ASCII 转换为字符串,也许它是有语义的。

分享工具

网上有些现成的进制转换工具,但都不好用。而且有广告,比较慢。


所以我自己做了一个工具: https://tool.hullqin.cn/byte-parser.html



特性如下:


  • 支持「十进制表示的 uint8 数组、二进制表示的 uint8 数组、十六进制表示的 uint8 数组、整体作为数字、ASCII 数组」互相转换。任意输入一项,只要合法,其它项都会自动生成,并且格式化,并且生成的结果你可以随意编辑,再次转换。

  • 如果出错,有错误提示,并保留上一次结果。

  • 如果字节过多(超过 6),那么(整体的值)10 进制意义不大,就不再计算了。

  • 如果有一个字节不是合法的 ASCII,就认为 ASCII 无意义,就不再计算了。

  • 轻量级、开源,不到 200 行代码,如有定制化需求,可 fork 代码后修改。

如何开发

核心 html:


<div>  <label for="bytes-10">(uin8数组)10进制:</label>  <br/>  <textarea id="bytes-10"></textarea></div><div>  <label for="bytes-2">(uin8数组)2进制:</label>  <br/>  <textarea id="bytes-2"></textarea></div><div>  <label for="bytes-16">(uin8数组)16进制:</label>  <br/>  <textarea id="bytes-16"></textarea></div><div>  <label for="value-int">(整体的值)10进制:</label>  <br/>  <textarea id="value-int"></textarea></div><div>  <label for="value-ascii">(整体的值)ASCII:</label>  <br/>  <textarea id="value-ascii"></textarea></div><div id="message"></div>
复制代码


提前做好 dom 查询,存储下来,避免以后再查:


let bytes = [];const bytesEle = {  2: document.getElementById('bytes-2'),  10: document.getElementById('bytes-10'),  16: document.getElementById('bytes-16'),};const valueIntEle = document.getElementById('value-int');const valueAsciiEle = document.getElementById('value-ascii');const messageEle = document.getElementById('message');const pad = {  2: 8,  16: 2,  10: 0,};
复制代码


手写 setter,即设置 bytes 的函数:


const setBytes = (value) => {  bytes = value;  console.log(bytes);  Object.keys(bytesEle).forEach(radix => {    const ele = bytesEle[radix];    ele.value = bytes.map(i => i.toString(radix).padStart(pad[radix], '0').padStart(8, ' ')).join(' ')  });  if (bytes.length > 6) {    valueIntEle.value = '';  } else {    valueIntEle.value = bytes.map((i, index) => i * (256 ** (bytes.length - 1 - index))).reduce((a, b) => a + b).toString();  }  if (bytes.every(i => (i === 10 || i > 31) && i < 127)) {    valueAsciiEle.value = String.fromCharCode(...bytes);  } else {    valueAsciiEle.value = '';  }};
复制代码


手写 click 触发的事件,根据输入内容,生成 bytes,然后调用 setBytes,触发视图更新:


const changeBytesByUin8 = (value, radix) => {  const newBytes = value.split(/[^\da-fA-F]+/).filter(i => i !== '').map(i => parseInt(i.trim(), radix));  if (newBytes.findIndex(i => Number.isNaN(i) || i >= 256) >= 0) {    throw new Error('解析失败');  }  setBytes(newBytes);};const changeBytesByInt = (value) => {  let num = Number(value.trim());  if (Number.isNaN(num)) {    throw new Error('解析失败');  }  const result = [];  while (num > 0) {    result.splice(0, 0, num % 256);    num = Math.floor(num / 256);  }  setBytes(result);};const changeBytesByAscii = (value) => {  const result = Array.from(value).map(i => i.charCodeAt(0));  if (result.findIndex(i => i > 256) >= 0) {    throw new Error('解析失败');  }  setBytes(result);};
复制代码


把事件绑定到 dom 上:


const onUint8ValueChange = (radix) => (event) => {  try {    messageEle.innerText = '';    changeBytesByUin8(event.target.value.trim(), radix);  } catch (e) {    messageEle.innerText = e.message;  }}Object.keys(bytesEle).forEach(radix => {  bytesEle[radix].addEventListener('change', onUint8ValueChange(radix));});
复制代码

体验地址 & 源码

体验地址: https://tool.hullqin.cn/byte-parser.html


源码: https://github.com/HullQin/tool-hullqin-cn

写在最后

我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

发布于: 2022 年 08 月 31 日阅读数: 23
用户头像

HullQin

关注

公众号【线下聚会游戏】 2020.10.07 加入

game.hullqin.cn 我做了一些联机桌游网页:支持2-10人联机的UNO、2-4人联机的斗地主、2人联机的五子棋。无需下载,点开即玩!叫上朋友,即刻开局!不看广告,不做任务,享受「纯粹」的游戏!

评论

发布
暂无评论
[JS入门到进阶] 手写解析uin8数组的工具:解析二进制字节,太快太方便了!_CSS_HullQin_InfoQ写作社区