我是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 噢~我有空了会分享做游戏的相干技术。