ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

易语言二进制转十进制:从原理到实战的完整指南

易语言二进制转十进制:从原理到实战的完整指南 说实话二进制转十进制在易语言里属于那种“看起来幼儿园难度真上手全是坑”的活儿。我最早碰它是在做串口通信的时候——从设备寄存器里读回来一串字节每个字节的各个位全是标志状态0和1密得像天书。总不能让操作工抱个计算器一个一个按吧。于是就得写个小工具把“10110101”这种字符串直接转成数值显示在界面上。这篇东西不打算只丢一段代码给你我把原理、三种不同写法、还有我在实战里踩过的一堆坑全部整理出来。适合刚学易语言的新手也适合那些整天做上位机、搞协议解析但从来没深究过进制转换的老手。1. 为什么你绕不开二进制转十进制1.1 真实场景串口、文件解析和位标志易语言写上位机的人应该都有同感硬件设备几乎没有直接返回十进制数的尤其是Modbus、传感器、PLC这类设备寄存器里存的都是位bit的组合。比如一个状态寄存器有8个位每一位代表“电机是否故障”“限位是否触发”“门是否关好”你读回来的原始字节实际上是二进制但人眼看去就是一堆0和1。你要把这些位解析成真正的数字状态最直接的办法就是把二进制文本片段转成十进制值再进行判断。还有一类场景是文件解析比如BMP图片文件头、某些自定义的二进制存档格式里面大量字段是以二进制位的形式存放的。你想读出其中某个字段的数值就得先把那一串0和1切出来再转成整数型。另外还有一个容易忽略的场景程序之间传参数。很多第三方接口传回来的所谓“字符串参数”本质是二进制数的文本形态比如“01100011”代表十进制99。你不转后面所有逻辑都卡死。1.2 方案选型内置命令还是自己写算法易语言有不少模块和扩展支持库提供了进制转换但核心库其实没有直接给出“二进制文本到十进制”的一行命令。网上随便搜“易语言 二进制转十进制”出来的都是各种模块的调用示例下载模块、注册模块、再调用子程序遇到模块没适配你当前易语言版本比如易语言5.9核心库变动一顿操作下来直接程序自动退出。我自己的原则很简单这种基础算法能自己写就自己写依赖最少受版本影响最小改造成其他进制也方便。而且二进制转十进制的算法本身不复杂完全手写也就十几行代码不涉及复杂数学。值得一提的还有有人问过我能不能用易语言opencv处理图像二值化后转二进制——思路是通的图像二值化后每个像素本来就是0和255转成0和1序列之后再按像素位拼成字符串最终用这套转换逻辑还原成灰度值。一个基础函数能串起这么多场景值得好好掌握。2. 核心原理二进制的数学本质与破解思路2.1 权值累加法从右边算到左边先聊最基础的概念。二进制只有0和1两个数字从右往左每一位的“权”是2的0次方、2的1次方、2的2次方……一直往上翻倍。你把第n位上的数字乘以2的n次方然后把所有位的结果加在一起就是十进制值。举个例子二进制10110101从右往左数位下标从右往左的位权值(2的次方)该位贡献01111020214430804116165032061646471128128合计 1 4 16 64 128 213。这个算法在数学上完全正确逻辑也最好理解适合新手入门时用来对照。但是这里有个问题每次计算都要重新算一遍2的幂代码会多一个幂运算的循环而且要在循环里维护一个“当前权值”变量初看容易绕晕。所以实际写代码时我推荐下面这种从左边算的高效写法。2.2 为什么不直接用“到整数()”命令很多初学者看到“到整数()”就以为能搞定实际上到整数 (“10110101”)返回的结果是10110101这个普通数字不是213。两者的差别太大了到整数只是把字符串当成十进制的数字文本去解析它根本不关心你这是二进制还是十六进制。类型转换和进制转换是两码事新手最容易在这里卡壳。正确思路应该是把二进制文本当成一种“自定义编码”逐位读取、逐位累加。核心公式是下面这个从左到右遍历每一位维护一个计算结果 result每读一位把旧的 result 乘以2再加上当前这一位的数字全部读完result 就是对应十进制值这就是秦九韶算法在二进制上的应用也叫逐位递推法。你可以把它理解为“每次左移一位腾出空位再把新数字塞进去”和手算类似但少了很多重复计算。由于它不依赖2的幂运算代码简洁运行速度也更快我在实际工程里一直用这个版本。刚才那个10110101用这个方法验证一遍0×211、1×202、2×215、5×2111、11×2022、22×2145、45×2090、90×21181。咦你会发现这里的结果是181而我上面权值累加算出来的例子也是181。这正好说明同一个二进制数从右往左和从左往右两种算法殊途同归。我之前给的表格里那一位标错了实际10110101最低位是1所以从右往左权值累加最终也是181。你只要记住两种算法遇到同一个输入结果必须完全一致可以用来互相验证代码有没有写错。2.3 位运算版更快更贴近底层的写法如果你对易语言的位运算符比较熟还可以用“左移加加法”的方式实现。易语言核心库里有左移()这个命令把一个数整体向左移动一位低位自动补0。于是上面的递推过程可以写成result 左移(result, 1) 当前位数值。当前位数值就是0或1最后加进去正好填上左移空出来的最低位。这个写法实际上模拟的是计算机内部真正在做的事情——CPU里根本没有“按权展开求和”它就是把寄存器左移一位然后加上新bit。所以理解了位运算版你对系统底层就多了一层感觉以后看内存数据、调试和逆向分析都更有底气。性能上两者差距在短字符串上几乎感觉不出来但如果你的程序需要一次性转换几万条二进制数据位运算版会更稳定。3. 完整实操从界面到代码手写一个转换器3.1 新建易语言工程与界面布局我用易语言5.9为例新建一个Windows窗口程序把窗口标题改成“二进制转十进制小工具”。界面只需要三个组件编辑框1用来输入二进制字符串编辑框2用来显示十进制结果可设置“只读”按钮1标题改成“转换”做上位机界面时我习惯先把控件位置调整好再写代码。如果你追求好看的自绘窗口可以用异形支持库(xwindow)做无边框皮肤但那只是外观层面的东西不影响转换逻辑新手不用在这上面纠结。一个实用的习惯是给编辑框1设置“输入方式”为“整数”这样软键盘和中文输入法的一些干扰会少一些。不过注意这个设置只影响输入法行为无法真正限制用户输入字母所以代码层校验还是逃不掉的后面讲到。3.2 核心代码三种实现方式的完整源码方式一从左到右递推推荐新手用.版本 2 .程序集 窗口程序集_启动窗口 .子程序 _按钮1_被单击 .局部变量 二进制文本, 文本型 .局部变量 十进制结果, 长整数型 .局部变量 文本长度, 整数型 .局部变量 计次, 整数型 .局部变量 当前字符, 文本型 .局部变量 当前值, 整数型 二进制文本 编辑框1.内容 十进制结果 0 文本长度 取文本长度 (二进制文本) .计次循环首 (文本长度, 计次) 当前字符 取文本中间 (二进制文本, 计次, 1) 如果真 (当前字符 “0”) 当前值 0 十进制结果 十进制结果 × 2 当前值 到循环尾 () 如果真结束 如果真 (当前字符 ≠ “1”) 信息框 (“输入只能包含0和1”, 0, , ) 返回 () 如果真结束 当前值 1 十进制结果 十进制结果 × 2 当前值 .计次循环尾 () 编辑框2.内容 到文本 (十进制结果)注意我没在0分支里多做处理是因为结果×20就是左移一位。这段代码的关键点是每次循环都执行结果×2当前值不管当前是0还是1都走一遍这个公式。中间夹着输入校验遇到非法字符直接弹窗并返回。方式二权值累加版便于理解但效率低如果你想对照权值累加原理写大概是这样.局部变量 权值, 长整数型 .局部变量 栈文本, 文本型 权值 1 十进制结果 0 .计次循环首 (文本长度, 计次) 当前字符 取文本中间 (二进制文本, 文本长度 计次 1, 1) 如果真 (当前字符 “1”) 十进制结果 十进制结果 权值 如果真结束 权值 权值 × 2 .计次循环尾 ()这段代码从右往左取字符每遇到一个1就加上当前权值权值每轮乘以2。代码很好懂但要额外维护一个权值变量而且权值增长很快一旦二进制文本超过31位就必须用长整数型否则直接溢出。实际项目里我还是建议用方式一。方式三位运算版性能最好.局部变量 当前值, 整数型 十进制结果 0 .计次循环首 (文本长度, 计次) 当前字符 取文本中间 (二进制文本, 计次, 1) 如果真 (当前字符 ≠ “0” 且 当前字符 ≠ “1”) 信息框 (“输入只能是0和1”, 0, , ) 返回 () 如果真结束 当前值 到整数 (当前字符) 十进制结果 左移 (十进制结果, 1) 当前值 .计次循环尾 ()左移命令在易语言核心库里直接有不用额外声明。如果换成右移加反向遍历也能实现从右往左的版本。位运算版的优势是直观映射底层寄存器的行为在大量重复转换时可以放进子程序里做成公开函数其他窗口直接调用。3.3 加一个“输入合法性校验”避免崩溃别小看输入校验。我做串口调试工具时用户直接把带换行符、带空格、甚至带中文符号的文本粘贴进来不做校验的程序直接卡死或者程序自动退出。所以校验这块必须放在循环内部而且要放在第一位运算之前。校验规则就两条每个字符必须是“0”或“1”整段文本不能为空空输入直接提示我在方式一的代码里已经演示了校验的写法。还有一个细节如果你使用取代码()命令判断字符码字符“0”的代码是48“1”的代码是49这样也可以判断合法性速度更快。但要记得兼容全角数字全角“”“”的字符码和半角不一样建议先做全角转半角处理。.子程序 全角转半角, 文本型 .参数 待处理文本, 文本型 .局部变量 返回文本, 文本型 .局部变量 计次, 整数型 .局部变量 代码, 整数型 返回文本 “” .计次循环首 (取文本长度 (待处理文本), 计次) 代码 取代码 (取文本中间 (待处理文本, 计次, 1)) 如果真 (代码 ≥ 65296 且 代码 ≤ 65305) 代码 代码 65248 如果真结束 返回文本 返回文本 字符 (代码) .计次循环尾 () 返回 (返回文本)这段辅助函数在很多文本处理项目里都能复用不仅仅是二进制转换。4. 避坑指南这些坑我全踩过4.1 整数型溢出超过31位就错易语言的整数型是32位有符号数最大值是2147483647。一个31位全是1的二进制数转换完已经接近这个值32位全是1则直接溢出变成负数。所以我的代码里所有存放结果的变量一律用“长整数型”它能放到64位有符号范围对应最大约9223372036854775807也就是最多处理63位二进制字符串。再长的输入就不建议用数值型了你可以改成科学计数、字符串分段处理或者直接用字节集保存中间结果。这个坑特别隐蔽短字符串测试一切正常一旦用户输入64位二进制程序不会崩溃但会返回一个莫名其妙的负值。排查半天还以为是算法错了实际上是数据类型不够宽。4.2 子线程操作UI控件导致程序自动退出很多做上位机的人会碰到这个问题转换的二进制数据很长放在界面线程里点按钮计算窗口卡住转圈。于是自然想到用启动线程开一个子线程去计算但子线程里直接写编辑框2.内容 结果结果程序秒退。原因很简单易语言的窗口UI控件绑定的是主线程子线程直接操作控件会触发内部资源冲突轻则控件显示异常重则直接闪退。正确做法是通过“标签反馈事件”把数据传回主线程。.子程序 _按钮1_被单击 .局部变量 线程句柄, 整数型 启动线程 (子线程转换, , 线程句柄) .子程序 子线程转换 .局部变量 结果, 长整数型 .局部变量 文本, 文本型 文本 编辑框1.内容 结果 二进制转十进制 (文本) 标签1.发送反馈事件 (结果, 0) .子程序 _标签1_反馈事件 .参数 参数一, 整数型 .参数 参数二, 整数型 编辑框2.内容 到文本 (参数一)注意子线程内部仍然可以读取编辑框1的内容这不算写操作一般没问题。但保险起见也可以把输入文本作为线程参数传进去完全隔离UI访问。标签反馈事件触发的时机由系统消息队列保证不会和主线程抢占控件这个方案在易语言社区是通用做法。4.3 空格、换行和全角符号混进输入实际用户不会像你一样规规矩矩输入“10110101”。他们会从Excel、日志文件、网页调试工具里复制数据混入空格、换行、制表符甚至不可见字符。如果不特殊处理第一个字符是换行符就直接触发非法输入。我的习惯是转换前先做一次去空白处理.子程序 去除空白字符, 文本型 .参数 源文本, 文本型 .局部变量 结果文本, 文本型 .局部变量 字符, 文本型 .局部变量 计次, 整数型 结果文本 “” .计次循环首 (取文本长度 (源文本), 计次) 字符 取文本中间 (源文本, 计次, 1) 如果真 (字符 ≠ “ ” 且 字符 ≠ #换行符 且 字符 ≠ 字符 (9)) 结果文本 结果文本 字符 如果真结束 .计次循环尾 () 返回 (结果文本)如果你从sqlite里读字段值拿来转换也要注意字段类型和编码。sqlite.读字段值的常见问题是读出来的是字节集或者带BLOB类型标记的文本直接进转换函数会得到乱码。建议先判断字段类型把字节集转成十六进制文本再根据需要转成二进制字符串处理。这个道理和“数据库里存的文本可能自带前后空格”是一样的数据源头的脏数据必须先清洗再解析。5. 扩展一条命令搞定任意进制以及批量转换5.1 通用进制转换函数从2到36进制都能用既然二进制转十进制的思路是“逐位递推乘基数”那么把这个基数从2换成8、10、16代码几乎不用变。我封装了一个通用函数.子程序 任意进制转十进制, 长整数型, 公开 .参数 进制文本, 文本型 .参数 基数, 整数型 .局部变量 结果, 长整数型 .局部变量 文本长度, 整数型 .局部变量 计次, 整数型 .局部变量 当前值, 整数型 .局部变量 当前字符, 文本型 结果 0 文本长度 取文本长度 (进制文本) .计次循环首 (文本长度, 计次) 当前字符 取文本中间 (进制文本, 计次, 1) 当前值 进制字符转数值 (当前字符) 如果真 (当前值 ≥ 基数 或 当前值 -1) 返回 (-1) 如果真结束 结果 结果 × 基数 当前值 .计次循环尾 () 返回 (结果) .子程序 进制字符转数值, 整数型 .参数 字符, 文本型 .局部变量 代码, 整数型 代码 取代码 (字符) 如果 (代码 ≥ 48 且 代码 ≤ 57) 返回 (代码 48) 否则 代码 代码 32 大写转小写 如果 (代码 ≥ 97 且 代码 ≤ 122) 返回 (代码 87) 否则 返回 (-1) 如果结束 如果结束这个函数传基数时传2就是二进制传8是八进制传16是十六进制。A-F自动识别大小写非法字符返回-1。有了它你在项目里处理八进制权限位、十六进制颜色值全都复用同一套逻辑。5.2 批量转换从文件、数据库读取二进制串很多项目不只要转换一次而是要批量处理几百上千条数据。你可以结合遍历指定文件名的技巧把某个目录下的所有日志文件读出来每五行提取一段二进制串批量转换以后写到新文件里。底层思路就是两层循环外层遍历文件名内层按行处理数据。.局部变量 文件数组, 文本型, , “0” .局部变量 计次, 整数型 文件数组 寻找文件 (取运行目录 () “\*.log”, 1, 真) .计次循环首 (取数组成员数 (文件数组), 计次) 读取文件内容提取二进制串调用转换函数 .计次循环尾 ()这里用到了寻找文件()配合数组收集文件名注意寻找文件()的第二参数为1表示返回完整路径。文件内容读取可以用读入文件命令拿到的是字节集你需要先转成文本再逐行处理。如果你用易语言自带的数据库支持库或外部ado数据库操作支持库1.4版读取数据同样道理读出来的字段值先做类型判断转成文本后再进入转换流程。核心转换函数保持独立不管数据来自文件、数据库还是网络都能直接调用。5.3 再进一步十进制转二进制反向操作转换器做成双向会遇到不少新需求。二进制转十进制的反向算法是“除2取余法”不断除以2把余数倒着拼起来。比如十进制1313÷26余1、6÷23余0、3÷21余1、1÷20余1余数从下往上读是1101。.子程序 十进制转二进制, 文本型 .参数 十进制数, 长整数型 .局部变量 结果, 文本型 结果 “” .判断循环首 (十进制数 0) 结果 到文本 (十进制数 2) 结果 十进制数 十进制数 ÷ 2 .判断循环尾 () 如果真 (结果 “”) 结果 “0” 如果真结束 返回 (结果)注意易语言的整数除法默认是整除十进制数 ÷ 2在长整数型上下文里会得到整数商不必担心小数。如果要支持负数可以先记录符号对绝对值转换最后拼上负号。这个函数可以作为通用子程序放进模块以后做位运算展示、协议收发工具都能用。我在实际使用中还有一个习惯写完转换核心代码以后用“输出调试文本”打印每一步中间结果在开发环境里观察循环执行情况。尤其当你把二进制字符串从sqlite读取、经过清洗、再进转换函数时中间任何一个环节出了问题调试输出都能帮你一眼定位。很多程序自动退出的问题排查到最后往往不是算法的问题而是数据源头的脏格式没有清理干净。核心算法保持简单数据清洗做扎实这套组合拳打下来你的转换工具才能真正在各种场景里稳定跑起来。
返回列表