草庐IT

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

HullQin 2023-03-28 原文
大家好,我是公众号「线下聚会游戏」作者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 噢~我有空了会分享做游戏的相关技术。

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

  1. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  4. ruby-on-rails - 我更新了 ruby​​ gems,现在到处都收到解析树错误和弃用警告! - 2

    简而言之错误:NOTE:Gem::SourceIndex#add_specisdeprecated,useSpecification.add_spec.Itwillberemovedonorafter2011-11-01.Gem::SourceIndex#add_speccalledfrom/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:91./opt/local/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/rails/gem_dependency.rb:275:in`==':und

  5. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  10. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

随机推荐