关于二进制:图片整理信息在计算机中的表示

信息在计算机中的示意:https://www.edrawmax.cn/online/share.html?code=25b9520ce72311...

April 30, 2023 · 1 min · jiezi

关于二进制:进制转换以及位运算

本文代码演示采纳的是golang语言进制// 二进制// go不能间接输入二进制,能够应用 %b 格式化// fmt %b 示意为二进制var n int = 5fmt.Printf("%T %b\n", n, n) // 101// 8进制, 0-7// 以数字0结尾示意var n1 int = 011fmt.Printf("%T %v\n", n1, n1) // 9// 16进制,0-9A-F,// 以0x或者0X结尾示意var n2 int = 0x11fmt.Printf("%T %v\n", n2, n2) // 17其它进制转十进制十进制数字计算$123 = 3 * 1 + 2 * 10 + 1 * 100$ 二进制转十进制从最低位开始,将每个位上的数提取进去,乘以2的位数-1次方,而后求和 $1011 = 1 * 1 + 1 * 2 + 0 * 4 + 1 * 8 = 1 + 2 + 0 + 8 = 11$ ...

October 26, 2022 · 2 min · jiezi

关于二进制:二进制转十进制的思路

public class ToBin { public static String toBinString1(int i) { return Integer.toBinaryString(i); } public static String toBinString2(int i) { //int 型是4个字节,32位,也就是说左移32次,每次移1位 StringBuilder builder = new StringBuilder(); int count = 32; while(count-- > 0) { builder.append((i & 1) == 1 ? "1":"0"); i = i>>1; } return builder.reverse().toString(); } public static void main(String[] args) { System.out.println(toBinString1(100)); System.out.println(toBinString2(100)); }}

November 29, 2021 · 1 min · jiezi

关于二进制:理解为什么8位有符号数的范围为128-127

8位无符号整型示意的数字范畴0~2558为有符号整型示意的数字范畴-128 ~ +127有的时候可能会不解,为什么不是 -127 ~ 127,-127 ~ 128等等 从无符号整型示意的范畴中能够晓得8位二进制能够示意256个数字,上面列出有符号的8位值局部示意 十进制 二进制原码 127 01111111 126 01111110 . . . . 0 00000000 -1 10000001-126 11111110-127 11111111-128 10000000最初-128用10000000示意,-127(11111111)加1 二进制符号位不变数字位溢出舍去为10000000,10000000能够了解为-0位示意-128值 以上为简略了解 参考https://blog.mimvp.com/articl...

November 20, 2021 · 1 min · jiezi

关于二进制:新手入门二进制安全和网络渗透该选哪个比较好

大家好我是周杰伦 1-方向抉择最近,有很多粉丝都不谋而合的问到了我一个问题:二进制平安和网络浸透,怎么选? 明天这篇文章,就是尝试解答这个问题,心愿对大家抉择方向上有所帮忙。 首先先来理分明,这两个方向别离是干嘛的。 一般来说,二进制平安方向,次要是做逆向剖析、破绽开掘与Exploit、内核平安、破解等工作。 网络浸透次要是Web平安、内网浸透、破绽扫描与利用、平安审计、日志剖析等工作。 简略来说,二进制方向,常常打交道的是软件、程序、指令、调试器、操作系统等货色;网络浸透方向,常常打交道的是服务器、漏扫工具、数据库、字典、日志等等。 当然,两个方向并不是齐全割裂的,在很多时候,二者可能存在交加,比方流量剖析。 2-学习难度在学习难度方面,我集体认为二进制平安方向会更平缓一些。 这个方向须要重点学习《C/C++编程》、《编译原理》、《操作系统》、《计算机组成原理》、《计算机网络》等泛滥计算机专业课程,而后联合学习逆向剖析技术、破绽攻打技术等常识。这外面波及到的常识都是有肯定的难度,老手小白须要操之过急,从编程学起,没有一年功夫很难入门。 上面是我之前在看雪网看到的一张二进制破绽方向的技能学习图谱,分享给大家: 而网络浸透方向相对来说,对零根底的小白更加敌对,学一些简略的计算机网络常识和Web编程,就能够上手了,不须要懂太多计算机底层的原理技术。 但留神这只是上手,这个方向前面要学的货色并不比二进制平安要少,Web平安、Linux、网络协议攻打、各种网络服务器/中间件的日志剖析、破绽扫描、提权技术、内网转移等等。 网络浸透方向的一个特点是广而杂,须要学习计算机、网络的方方面面。而二进制平安方向相对而言会显得更加专一。 3-待业待业方面而言,网络浸透相对来说岗位会更多一些,毕竟不是所有互联网公司都有操作系统、客户端产品,但所有互联网公司都有网络服务器。 二进制平安方向次要是一些大厂的样本剖析、平安钻研,或者一些游戏公司的内核平安、驱动开发岗位。 而基本上所有的大厂都会有本人的SRC,也就是应急响应核心,就会有很多平安岗位,同时各个平安厂商作为乙方也会有很多平安服务岗位、浸透测试工程师岗位,所以在待业方面,网络浸透方向会有更多的抉择机会。 总结下来,二进制平安岗位学习难度大,而且待业抉择不如网络浸透多,如果在没有显著的趣味偏好的前提下,我会优先举荐学习网络浸透方向。这个方向还有一个劣势就是因为学习的面很广,会接触到方方面面的货色,即使当前你想转到开发岗或者运维等其余岗位也是比拟容易的。 当然,二进制平安方向这条路,并不是不可取,如果你对程序逆向,破绽攻打有特地的趣味,可能熬过一段时间的光明摸索期,也会迎来属于本人的光明,这个方向,因为难度高,违心进入的人也少,一旦学有所成,也是十分吃香的。 当然,以上只代表我集体的观点和认识,欢送大家在留言区评论交换,说出你的抉择与认识。

August 26, 2021 · 1 min · jiezi

关于二进制:教你如何将二进制文件导入到数据库

摘要:应用copymanager接口,读取二进制文件流,将二进制文件中的数据导入到数据库中。本文分享自华为云社区《从二进制文件导入到GaussDB(DWS)》,作者:你怎么这么难看。 1.1 现网业务场景源数据推送二进制流-->解析二进制-->解析后的数据导入数据库 为了模仿生产的业务场景,客户提供了一个二进制文件及二进制文件的解析程序,须要咱们解析二进制文件后导入数据库。 1.2 测试计划因为客户给出的解析程序是单条解析,为了晋升数据导入的性能,须要微批导入的形式,在内存中积攒一定量的数据后,再调用copymanager接口导入数据库中。最终测试计划的整体流程如下所示: 1.3 测试过程从上图能够看出,一批数据的耗时由三局部组成: 解析二进制文件积攒一批数据数据导入数据库1)解析二进制文件 这是客户提供的Java程序,按行读取二进制文件中的数据,每行解析成一个String对象。 2)积攒数据 积攒的数据最终是要通过copymanager接口导入数据库中的,因而其对象类型须要是copymanager接口能够调用的。从下图中能够看出,其对象类型为InputSream。 初步采纳的解决方案是将解析后的String对象拼接起来,而后在copymanager接口中将String转为InputStream。在测试过程中发现,因为String类型在解决这种大对象的时候效率较低随着积攒数据的增多,这部分耗时越来越久,因而,最终计划是将解析后的数据寄存到StringBuffer对象中。 3)调用copymanager接口 在Java中援用GaussDB(DWS)安装包中的JDBC驱动后,须要import CopyManager及BaseConnection import org.postgresql.copy.CopyManager;import org.postgresql.core.BaseConnection; 在创立copymanager对象的时候,须要将Connection对象转为BaseConnection,如下所示: CopyManager copyManager = new CopyManager((BaseConnection)conn); 1.4 测试后果在单连贯的状况下,解析+入库可解决10.1W/S,导入性能可达到64.5W/S; 在255并发状况下,解析+入库可解决1252.1 W/S,导入性能可达到4132.4W/S。 1.5 总结在本地执行测试代码在本地用idle调试中须要一个二进制文件,能够从客户给出的文件中执行head –n 1000 *.dat>1.dat 截取一部分文件内容放在本地。 各个工夫收集为了更好的给客户展示咱们数据库的弱小性能,须要把数据库导入的工夫独自列出来,给客户一个更加直观的感触。 高并发的设计首先,将并发程序安排在集群外的服务器上, 而后为了防止单盘IO性能瓶颈,在每块盘上寄存一份数据文件及解析程序。因为是把一批数据积攒到内存中的,因而在启动并发时须要留神内存的应用状况。 在客户端服务器并发75、内存曾经饱和的状况下,查看集群的cpu及内存使用率依然不高,因而,在集群外部的所有节点上每个减少30并发,达到最大并发数。 注意事项: JDBC连贯时,把所有并发平均分配到各个CN节点上;集群外部节点JDBC连贯时可能会报gss谬误,须要批改conf配置文件,将本地ip的连贯形式由gss改为sha256。想理解GuassDB(DWS)更多信息,欢送微信搜寻“GaussDB DWS”关注微信公众号,和您分享最新最全的PB级数仓黑科技,后盾还可获取泛滥学习材料~ 点击关注,第一工夫理解华为云陈腐技术~

July 26, 2021 · 1 min · jiezi

关于float:FloatDouble单双精度在线转换成二进制

在线浮点数转二进制在线浮点数转二进制 在线浮点数转二进制

April 21, 2021 · 1 min · jiezi

关于二进制:二进制反码补码

二进制、反码、补码前置浏览: C语言中文网-汇编语言基本概念简介-补码及进制转换根底概念有符号二进制整数有负数和正数。在 x86 处理器中,MSB 示意的是符号位:0 示意负数,1 示意正数。下图展现了 8 位的负数和正数: 负整数用补码(two\`s-complement)示意时,应用的数学原理是:一个整数的补码是其加法逆元。(如果将一个数与其加法逆元相加,后果为 0。)补码表示法对处理器设计者来说很有用,因为有了它就不须要用两套独立的电路来解决加法和减法。 例如,如果表达式为 A-B,则处理器就能够很不便地将其转换为加法表达式:A+(-B)。将一个二进制整数按位取反(求补)再加 1,就造成了它的补码,过程如下所示:1111 1111 是 -1 的补码。初始值00000001第一步:按位取反11111110第二步:将上一步失去的后果加 111111110 +00000001和值: 补码示意11111111概念总结: 反码、补码是二进制的一种表现形式;在计算机内所有数值底层都用补码示意,无论正负数(十进制);如果一串二进制值须要视为数值则须要将其视为补码;反码是十进制转二进制计算的一个过程即对一个十进制取补码的过程,个别用在正数转换规则上;反码能够通过二进制值按位取反失去(所有二进制位都取反);负数(十进制)的补码是其二进制自身,正数(十进制)的补码是十进制正数的绝对值求补码后取反码加一;示意负数的补码能够间接转成十进制,示意正数的补码想要转回十进制步骤如下: 对示意正数的补码取反码加一失去正数的十进制绝对值补码;再将正数的十进制绝对值补码转成十进制失去正数的十进制绝对值;最初加上符号位;无论是负数加负数(十进制加法)还是负数/正数加正数(十进制减法)都能够用补码加补码示意;反码反码能够通过二进制值按位取反失去(所有二进制位都取反)负数的反码示例: 十进制数值补码反码00000 00001111 111110000 00011111 111020000 00101111 110130000 00111111 110040000 01001111 1011正数的反码示例: 十进制数值补码反码-00000 00001111 1111-11111 11110000 0000-21111 11100000 0001-31111 11010000 0010补码(十进制转二进制)在计算机内所有数值底层都用补码示意,无论正负数(十进制)十进制数值补码00000 000010000 000120000 001030000 0011-00000 0000-11111 1111-21111 1110-31111 1101正数补码计算过程示例: 十进制数值绝对值绝对值补码绝对值补码取反绝对值补码取反加一正确补码十进制数值-000000 00001111 11111111 1111+ 1—————1,0000 00000000 0000-0-110000 00011111 11101111 1110+ 1—————1111 11111111 1111-1-220000 00101111 11011111 1101+ 1—————1111 11101111 1110-2-330000 00111111 11001111 1100+ 1—————1111 11011111 1101-3-440000 01001111 10111111 1011+ 1—————1111 11001111 1100-4-550000 01011111 10101111 1010+ 1—————1111 10111111 1011-5补码(二进制转十进制)示意负数的补码能够间接转成十进制,示意正数的补码想要转回十进制步骤如下: ...

April 3, 2021 · 2 min · jiezi

关于二进制:二进制-零扩展与符号扩展

概述学计算机组成的时候遇到了,就写一下本人的了解 利用场景在计算机组成中,少位二进制数和多位二进制数进行运算时,须要用到 如1111(4位二进制)与11001100(8位二进制)相加或者其它运算时,4位的二进制须要进行扩大,能力与8位二进制运算零扩大高位间接补0的扩大,如1111变成00001111,补0并不影响计算结果,这个很好了解,但如果二进制数带了符号,就不一样了,因为最高位是符号位,所以1111就总一个正数,变成了一个负数00001111,由此,产生了符号扩大 符号扩大高位间接补符号位,如0111扩大成00001111,而1000扩大成11111000,而且它也不影响计算结果,这里看着有点难了解了你能够试着计算一下 1111+1000=011100001111+11111000=00000111去掉扩大的符号,还是原来的滋味

October 26, 2020 · 1 min · jiezi

关于二进制:原码反码补码之128127的由来

https://blog.csdn.net/yuechao...

September 18, 2020 · 1 min · jiezi

webSocket 二进制传输基础准备-UTF-16和UTF-8转Unicode

前言今天来学习UTF8转Unicode,UTF16转Unicode以达成UTF8,UTF16之间的互转。提炼成函数的公式我并没有放出来,我的目的只是为了更加理解 字符编码之间的关系。如果你需要转码方式,可以找其他的库,或者根据我文章来进行提炼。基本利用按位操作符 符号运算符就可以完成。今天这里只做UTF8转Unicode,UTF16转Unicode, 后续转换可以看前面的文章。1.基础准备工作2.Unicode转UTF83.Unicode转UTF16UTF16转Unicode为了更好的理解,我们来使用Unicode转UTF-16那一期的结果来进行UTF16转Unicode,U+22222转UTF-16 = [0xd848,0xde22] = ‘????’(这个字的长度为二,所以要获取他所有的charCodeAt)function charCodeAt(str){ var length = str.length, num = 0, utf16Arr = []; for(num; num < length; num++){ utf16Arr[num] = ‘0x’+str[num].charCodeAt().toString(16); } return utf16Arr;}charCodeAt(’????’);//[‘0xD848’, ‘0xDE22’]计算utf-16 4字节的取值范围上面代码获得了,这个字符的UTF-16编码数组,JS的字符串全部使用的UTF-16编码格式回顾一下UTF-16的编码方式将Unicode值减去0x10000,得到20位长的值,再将其分为高10位和低10位,分别为2个字节,高10位和低10位的范围都在 0 ~ 0x3FF,高10位加0xD800,低十位加0xDC00首先我们先看字节问题,Unicode值在U+10000 ~ U+10FFFF时,会分为 两个2 字节,二进制 8位为一个字节,所以UTF-16的四个字节的字符是两个 16位的二进制并且根据UTF-16的编码方式的高位加0xD800 低位加0xDC00得出最小范围值高10位最小值为0xD800,低10为最小值为0xDC00再根据 高10位和低10位的范围都在 0 ~ 0x3FF得出最大范围值高10位最大值为0xD800+0x3FF,低10为最大值为0xDC00+0x3FF所以高10位的取值范围为 0xD800 ~ 0xdbff低10位的取值范围为 高10位的取值范围为 0xDC00 ~ 0xdfff我们已经得知了UTF16编码的高10位和低10位的取值范围所以可以进行判断 是否需要进行逆推转换var strCode = charCodeAt(’????’), strCode0 = strCode[0], strCode1 = strCode[1];if(strCode0 >= 0xD800 && strCode0 <= 0xDBFF){ if(strCode1 !=undefined && strCode1 >= 0xDC00 && strCode1 <= 0xDFFF){ //到了这里说明这个字符是四个字节就可以开始进行逆推了 //高10位加0xD800,低十位加0xDC00,所以减去 strCode0 = strCode0 - 0xD800 = 0xd848 - 0xD800 = 0x48 = 72; strCode1 = strCode1 - 0xDC00 = 0xDE22 - 0xDC00 = 0x222 = 546; //高10位和低10位进行拼接。 字符串或者乘法都行 //1 字符串的方式拼接 我用抽象的方式来展现过程 strCode0.toString(2)+strCode1.toString(2) = ‘1001000’ + ‘1000100010’ = ‘10010001000100010’ parseInt(Number(‘10010001000100010’),2).toString(16)//74274 = 0x12222 //Unicode转utf16时 将Unicode值减去0x10000,所以再进行加法 0x12222 + 0x10000 = 0x22222; //答案是不是昨天选择的值呢 //2 利用数学的方式进行转换 //先给高10位从末位补10个0,也就是乘以10000000000(二进制) = 0x400(16进制) = 1024(十进制) strCode00x400 = 0x480x400 = 1001000*10000000000 = 1001000 10000000000 = 0x12000 //再加上减去0xDC00后的低10位 0x12000+0x222 = 0x12222 //加上 Unicode转utf16时 将Unicode值减去的0x10000 0x12222+0x10000 = 0x22222; //Unicode U+22222 = ‘????’; return; }}//不满足上面条件时,说明UTF16转Unicode 等于原值。不懂为什么就回顾上期的表格UTF8转Unicode这里一样 使用Unicode转UTF8那期例子运算出的结果[0xe4, 0xb8,0x80]进行转换由于JS环境的字符串是UTF16编码所以我这里直接使用十六进制串来进行转换怎么判断二进制数据的字符是utf8中的几字节根据数据的第一个字节来进行判断 这个字符是几个字节。根据表格找到编码规则,用来区分这个数据串的字符是几字节js是使用小端存储的,小端存储是符合我们的逻辑,大端是逻辑相反大小端模式比如 小端存储是0xxx xxxx 大端存储就是相反的 xxxx xxx0utf8编码规则1 字节 0xxx xxxx2 字节 110x xxxx 10xxxxxx3 字节 1110 xxxx 10xxxxxx 10xxxxxx4 字节 1111 0xxx 10xxxxxxx 10xxxxxx 10xxxxxxjs是小端存储所以只需要按字节位进行对比即可。utf8各字节编码规则鲜明差异比较大的是首个字节,所以只需要对比首个字节,就可得知是几个字节。对比规则根据 按位与的特性,将原码的x对应,编码规则的位值转为0其他位保持不变(若有更好的判断方法,非常期待您的留言)也可以使用 带符号右移操作符 >>(js并不会转换正负符号 所以可以进行放心使用)对应编码规则右移n个位来进行判断值是否为0,110,1111。(只是猜想之一,并没有进行实际验证,目前仅实践了下面的方式)推导过程根据按位与用 1 来保留原码对应的编码规则位值以及x位值全部转换为0 来进行判断是几字节 二进制 将x替换为0 十六进制1字节 char0 & 1xxx xxxx = 0xxx xxxx char0 & 1000 0000 = 0000 0000 char0 & 0x80 = 02字节 char0 & 111x xxxx = 110x xxxx char0 & 1110 0000 = 1100 0000 char0 & 0xE0 = 0xC03字节 char0 & 1111 xxxx = 1110 xxxx char0 & 1111 0000 = 1110 0000 char0 & 0xF0 = 0xE04字节 char0 & 1111 1xxx = 1111 0xxx char0 & 1111 1000 = 1111 0000 char0 & 0xF8 = 0xF0上面的判断规则已经非常明了。下面的转码 我就只进行三字节的转码规则,其他 若有兴趣,可自行参考3字节的方式进行推算(动手才是理解最好的方式)var buffer = new ArrayBuffer(6);var view = new DataView(buffer);view.setUint8(0,0xe4);view.setUint8(1,0xb8);view.setUint8(2,0x80);view.setUint8(3,0xe4);view.setUint8(4,0xb8);view.setUint8(5,0x80);//[[Uint8Array]]: Uint8Array(6) [228, 184, 128, 228, 184, 128]var byteOffset = 0,//起点从1开始 char0, length = view.byteLength;//获取数据的字节数while(byteOffset <length){ var char0 = view.getUint8(byteOffset),char1,char2,char3; if((char0 & 0x80) == 0){ //代表是一个字节 byteOffset++; continue; } if((char0 & 0xE0) == 0xC0){ //代表是2个字节 byteOffset+=2; continue; } if((char0 & 0xF0) == 0xE0){ //代表是3个字节 //3 字节编码规则 1110 xxxx 10xxxxxx 10xxxxxx //进入这个区间时,char0是符合 1110 xxxx的规则的 //利用按位与来进行截取char0对应编码规则x的位值 也就是 0000 1111 0xF //我们先转换第一个字节,二进制 速算法 先将二进制进行转换16进制(4位二进制为一位十六进制) //228 & 0xF 1110 0100 & 0000 1111 = 100 = 0x4 = 4 char0 = char0 & 0xF = 4 //第二字节进行转换 //第二字节编码规则 10xx xxxx 同理利用按位与 0011 1111 0x3F //184 & 0x3F 1011 1000 & 0011 1111 = 11 1000 = 0x38 = 56 char1 = view.getUint8(byteOffset++); char1 = char1 & 0x3F = 56 //第三字节进行转换 //第三字节编码规则 10xx xxxx 同理利用按位与 0011 1111 0x3F //128 & 0x3F 1000 0000 & 0011 1111 = 00 0000 = 0x00 = 0 char2 = view.getUint8(byteOffset++) char2 = char2& 0x3F = 0 //下面才是重点,我们已经按字节转码完成 那么如何进行组合呢。 //第一种方法,利用字符串进行拼接。 //‘100’ + ‘11 1000’ + ‘00 0000’ = ‘0100 1110 0000 0000’ //parseInt(100111000000000,2) = 19968 //String.fromCharCode(19968) = ‘一’ //上面 我抽象的用二进制的过程来展现的,但是实际转换中 是看不到二进制的。 //parseInt(Number(char0.toString(2) + char1.toString(2) + char2.toString(2)),2) //第二种方式,利用左移操作符 << //编码规则 1110 xxxx 10xxxxxx 10xxxxxx 第一字节后面有12个x 所以第一字节末位补12个0 //char0 >> 12 = 4 >> 12 //00000000 00000000 00000000 00000100 >> 12 = 0100 0000 0000 0000 = 0x4000 = 16384 //第二自己后面有6个x 所以第二字节补6个0 //char1 >> 6 = 56 >> 12 //00000000 00000000 00000000 00111000 >> 6 = 1110 0000 0000 = 0xE00 = 3584 //第三字节为最后一个字节所以不需要末位补0 //利用按位或 进行组合 16384 | 3584 | 0 = 0100 1110 0000 0000 = 0x4e00 = 19968 //19968 < 0x10000(U+10000),不需要进行转码,调用String.fromCharCode即可 //Unicode码就转换完成了。 continue; } if( (char0 & 0xF8) == 0xF0){ //代表是4个字节 byteOffset+=4; continue; } throw RangeError(‘引用错误’);}这里编码转换就完成了。 ...

April 10, 2019 · 3 min · jiezi

webSocket 二进制传输基础准备-Unicode转UTF16

前言1.websocket 二进制数据传输基础准备工作2.webSocket 二进制传输基础准备-Unicode转UTF83.webSocket 二进制传输基础准备-Unicode转UTF16昨天我们学习了Unicode转UTF8js中所有的string类型都是使用的UTF-16编码下面就直接开始吧完整的 Unicode 字符集UTF-16编码方式Unicode码范围UTF-16编码方式字节U+0000 ~ U+FFFF等于原值2U+10000 ~ U+10FFFF将Unicode值减去0x10000,得到20位长的值。<br/>再将其分为高10位和低10位,分别为2个字节。<br/>高10位和低10位的范围都在 0 ~ 0x3FF<br/>高10位加0xD800,低十位加0xDC004Unicode转UTF-16今天使用 U+22222(大于U+10000) 进行转码UTF16先进行减去0x100000x22222 - 0x10000 = 0x12222 = 1 0010 0010 0010 0010转换二进制并且分割位高低10位二进制1111111111 = 1023十进制利用按位与的特性获取低10位二进制1111111111 & 1 00100010 00100010 = 10 0010 0010十进制1023 & 0x12222 = 546低10位加0xDC00546 + 0xDC00 = 56866 = 0xde22利用带符号右移运算符以及按位与获取高10位0x12222 << 10 = 72 = 10010001001000 & 1111111111 = 100100072 & 1023 = 72高10位加0xD80072 + 0xD800 = 55368 = 0xd848U+22222编码转UTF-16 = [0xd848,0xde22]var str = “”;[0xd848,0xde22].forEach(item => { str +=String.fromCharCode(item)})console.log(str);明天就是假期了,尽可能的做出UTF-16与UTF-8 的互转。大概原理就是先将 字符 逆推转Unicode编码 然后再转你想要的编码格式 ...

April 4, 2019 · 1 min · jiezi

webSocket 二进制传输基础准备-UTF16 转UTF8

前言今天学习一下编码,先回顾一下昨天的基础准备工作。下面进行了解UTF-8与UTF-16的二进制编码方式。为啥要了解这个,因为js中所有是string类型都是使用UTF-16编码的因此我们与后端进行通信时,需要转换成与之一致的编码。(后端或者前端转换)UTF-8编码方式注: 1. Unicode码范围 用十六进制表示 3. 8位二进制为一字节Unicode码范围UTF-8编码方式占用字节U+0000 ~ U+007F0xxxxxxx1U+0080 ~ U+ 07FF110xxxxx 10xxxxxx2U+0800 ~ U+FFFF1110xxxx 10xxxxxx 10xxxxxx3U+10000 ~ U+10FFFF11110xxx 10xxxxxxx 10xxxxxx 10xxxxxx4Unicode码转换UTF-8Unicode编码表使用,转换到UTF-8编码在Unicode中汉字 “一”编码为U+4E00,“丁"编码为 U+4E01这样想必就看得懂表了下面进行开始转换吧回顾昨日的二进制与十六进制U+4E00用十六进制表示 0x4E00转换二进制,按位转换4 = 0011E = 14 = 11100 = 00000 = 00000x4E00 = 0100 1110 0000 0000 = 199680x0800< 0x4E00 < 0xFFFF 得出是三个字节。UTF-8三字节的编码方式从 0100 1110 0000 0000 变成 1110 xxxx 10 xxxxxx 10 xxxxxx格式由从末位到首位进行顺位插入的方式 0100 111000 0000001110 xxxx 10 xxxxxx 10 xxxxxx1110 0100 10 111000 10 000000其中利用js的按位操作符 符号操作符进行转换先替换原码(从末位到首位)的第7位8位第一字节utf-8 3字节中的第一字节格式 为 10 xxxxxx所有截取6位(原码与编码对应的 为x的位值,要保持不变, 编码其中的x位值全部为原码)先利用按位与的特性(全1为1 否则为0)进行截取原码的末6位二进制 111111 = 63十进制0100 1110 0000 0000 & 0000 0000 0011 1111 = 00000019968 & 63 = 000000UTF-8的第一字节格式为 10 xxxxxx所以利用按位或的特性(遇1为1,全0 为0)来变换UTF-8 3字节中的第一个字节的编码方式将x替代为0 得出 10 000000二进制 10 000000 = 128;按位或00 000000 | 10 000000 = 10 0000000 | 128 = 128第二个字节UTF-8 3字节中的第二字节依然是10 xxxxxx格式,所以只需要从第6位开始进行截取6位利用带符号右移操作符 a >> b 首位开始补 b 个 首位值 右侧舍去b个位先舍去末6位0100 1110 0000 0000 >> 6 = 000 000 0100 1110 0019968 >> 6 = 312利用按位与进行截取000 000 0100 1110 00 & 111111 = 111 000312 & 63 = 56继续利用按位或进行变换00 111000 | 10 000000 = 10 11100056 | 128 = 184第三字节utf-8 3字节的第三字节编码方式为 1110 xxxx 所以只需要从第12开始截取4位从末位第12位开始截取4位,利用带符号右移操作符 a >> b 首位开始补 b 个 首位值 右侧舍去b个位0100 1110 0000 0000 >> 12 = 000 000 0000 010019968 >> 12 = 4利用按位与进行截取 4位二进制 1111 = 150100 & 1111 = 01004 & 15 = 4;利用按位或进行变换二进制 1110 0000 = 2240000 0100 | 1110 0000 = 1110 01004 | 224 = 228三个字节组合起来228 184 128 = 1110 0100 1011 1000 1000 0000(二进制) = 14 4 11 8 8 0( 四位转一位十进制) =0xe4b880(十六进制)utf-16转utf-80x4E00 = 0xe4b880python编码转换b’\xe4\xb8\x80’.decode(‘utf-8’) = “一” ...

April 3, 2019 · 2 min · jiezi

webscoket 二进制数据传输基础准备工作

二进制与十六进制二进制用 0 1 表示 2= 10十六进制 前缀0x 用0123456789ABCDEF表示 2= 0x2二进制与十六进制的转换十六进制的每位 等于二进制的四位十六进制 0xF = 15(十进制) = 1111(二进制) 十六进制每位最大就是二进制的 1111 = 15(十进制)二进制2 = 10十六进制2 = 0x2 = 0010规律很清晰了吧20 = 10100(6位 口算得出要补齐8位)0001 01000001 = 10100 = 420 = 0001 0100 = 0x14二进制字节一个字节是8个二进制位0000 0000 - 1111 1111 = 0-255 代表一个字节可以表示256个符号UTF编码字节UTF-16UTF-16大部分使用两个字节编码,编码超出 65535 的使用四个字节0x0000 - 0xFFFF 两个字节0x010000 - 0x10FFFF 四个字节UTF-80x0000 - 0x007F 一个字节0x0080 - 0x07FF 两个字节0x0800 - 0xD7FF 、0xE000 - 0xFFFF 三个字节0x010000 - 0x10FFFF 四个字节注: Unicode在范围 D800-DFFF 中不存在任何字符有符号32位整数节所有的按位操作符的操作数都会被转成补码(two’s complement)形式的有符号32位整数。反码、补码 负数js中的二进制转换是不会转换符号的1的反码 补码 以及 -11 = 0000 0000 0000 0000 0000 0000 0000 0001反码 1 =0、0 =1 下面会讲述按位操作符 按位非 ~ 其实就是反码1111 1111 1111 1111 1111 1111 1111 1110补码后 末位+11111 1111 1111 1111 1111 1111 1111 1111 = -1按位操作符按位操作符(Bitwise operators) 将其操作数(operands)当作32位的比特序列(由0和1组成),而不是十进制、十六进制或八进制数值。例如,十进制数9,用二进制表示则为1001。按位操作符操作数字的二进制形式,但是返回值依然是标准的JavaScript数值。&、| 在转码中比较常用按位与 &1为真,0为假 全真则真 遇假为假js 内置方法var a = 1;a.toString(2);十转二2 = 103 = 11二进制进行按位与运算 从左到右1 && 1 = 10 && 1 = 02 & 3 = 10 = 2按位或 |1为真,0为假 遇真则真 全假为假8 = 10009 = 1001二进制按位或运算 从左到右1 || 1 = 10 || 0 = 00 || 0 = 00 || 1 = 1 8 | 9 = 1001 = 9按位异或 ^1为真,0为假。不同为真 相同为假4 = 1005 = 101二进制按位异或运算 从左到右1 1 = 00 0 = 00 1 = 14 ^ 5 = 001 = 1按位非 ~1为真,0为假 对每一项进行非操作,遇真则假,遇假则真。(速算, ~x =-1*x-1)6 = 00000000 00000000 00000000 00000110 11111111 11111111 11111111 11111001 = -7按位移动操作符有两个操作数 左侧为要被移动的数字(十进制,但是操作是内部对二进制操作)右侧为移动的长度方向根据操作符移动,返回根据被操作数相同类型的结果,必须小于32位左移操作符 <<a << b 右侧末位开始补 b个0 左侧舍去b个位11 = 00000000 00000000 00000000 0000101111 << 2 右侧末位开始补 b个000000000 00000000 00000000 00001011 00 左侧舍去b个位000000 00000000 00000000 00001011 00格式化00000000 00000000 00000000 00101100 = 44-44 = (反码)11111111 11111111 11111111 11010011(补码)11111111 11111111 11111111 11010100-11 =(反码)11111111 11111111 11111111 11110100 (补码)11111111 11111111 11111111 11110101-11 << 2右侧末位开始补 b个0 11111111 11111111 11111111 11110101 00 左侧舍去b个位111111 11111111 11111111 11110101 00格式化11111111 11111111 11111111 11010100 = -44带符号右移操作符( 记住左侧首位 0 代表正 ,1 代表负) >>a >> b 左侧首位开始补 b 个 首位值 右侧侧舍去b个位12 = 00000000 00000000 00000000 0000110012 >> 2补 b 个 首位值 00 00000000 00000000 00000000 00001100右侧舍去b个位00 00000000 00000000 00000000 000011格式化00000000 00000000 00000000 00000011 = 3-3 = (反码)11111111 11111111 11111111 11111100(补码)11111111 11111111 11111111 11111101-12 >> 2-12 = (反码)11111111 11111111 11111111 11110011(补码)11111111 11111111 11111111 11110100-12 >> 2补 b 个 首位值 11 11111111 11111111 11111111 11110100右侧舍去b个位11 11111111 11111111 11111111 111101格式化11111111 11111111 11111111 11111101 = -3 ...

April 2, 2019 · 2 min · jiezi

一个函数让你看懂 'Why 0.1+0.2!=0.3'

话不多说,先上代码 function judgeFloat(n, m) { const binaryN = n.toString(2); const binaryM = m.toString(2); console.log(${n}的二进制是 ${binaryN}); console.log(${m}的二进制是 ${binaryM}); const MN = m + n; const accuracyMN = (m * 100 + n * 100) / 100; const binaryMN = MN.toString(2); const accuracyBinaryMN = accuracyMN.toString(2); console.log(${n}+${m}的二进制是${binaryMN}); console.log(${accuracyMN}的二进制是 ${accuracyBinaryMN}); console.log(${n}+${m}的二进制再转成十进制是${to10(binaryMN)}); console.log(${accuracyMN}的二进制是再转成十进制是${to10(accuracyBinaryMN)}); console.log(${n}+${m}在js中计算是${(to10(binaryMN) === to10(accuracyBinaryMN)) ? '' : '不'}准确的); } function to10(n) { const pre = (n.split(’.’)[0] - 0).toString(2); const arr = n.split(’.’)[1].split(’’); let i = 0; let result = 0; while (i < arr.length) { result += arr[i] * Math.pow(2, -(i + 1)); i++; } return result; } judgeFloat(0.1, 0.2); judgeFloat(0.6, 0.7);由于JavaScript中没有将小数的二进制转换成十进制的方法,于是手动实现了一个。先来一个简单的结论计算机中所有的数据都是以二进制存储的,所以在计算时计算机要把数据先转换成二进制进行计算,然后在把计算结果转换成十进制。由上面的代码不难看出,在计算0.1+0.2时,二进制计算发生了精度丢失,导致再转换成十进制后和预计的结果不符。其实有些标题党了,一个函数并不能让你深入理解,还得继续看下面…对结果的分析—更多的问题0.1和0.2的二进制都是以1100无限循环的小数,下面逐个来看JS帮我们计算所得的结果:0.1的二进制:0.00011001100110011001100110011001100110011001100110011010.2的二进制:0.001100110011001100110011001100110011001100110011001101理论上讲,由上面的结果相加应该::0.0100110011001100110011001100110011001100110011001100111实际JS计算得到的0.1+0.2的二进制0.0100110011001100110011001100110011001100110011001101作为一个代码强迫症的我又产生的新的问题:Why js计算出的 0.1的二进制 是这么多位而不是更多位???Why js计算的(0.1+0.2)的二进制和我们自己计算的(0.1+0.2)的二进制结果不一样呢???Why 0.1的二进制 + 0.2的二进制 != 0.3的二进制???js对二进制小数的存储方式小数的二进制大多数都是无限循环的,JavaScript是怎么来存储他们的呢?在ECMAScript®语言规范中可以看到,ECMAScript中的Number类型遵循IEEE 754标准。使用64位固定长度来表示。事实上有很多语言的数字类型都遵循这个标准,例如JAVA,所以很多语言同样有着上面同样的问题。所以下次遇到这种问题不要上来就喷JavaScript…有兴趣可以看看下这个网站http://0.30000000000000004.com/,是的,你没看错,就是http://0.30000000000000004.com/!!!IEEE 754IEEE754标准包含一组实数的二进制表示法。它有三部分组成:符号位指数位尾数位三种精度的浮点数各个部分位数如下:JavaScript使用的是64位双精度浮点数编码,所以它的符号位占1位,指数位占11位,尾数位占52位。下面我们在理解下什么是符号位、指数位、尾数位,以0.1为例:它的二进制为:0.0001100110011001100…为了节省存储空间,在计算机中它是以科学计数法表示的,也就是1.100110011001100… X 2-4如果这里不好理解可以想一下十进制的数:1100的科学计数法为11 X 102所以:符号位就是标识正负的,1表示负,0表示正;指数位存储科学计数法的指数;尾数位存储科学计数法后的有效数字;所以我们通常看到的二进制,其实是计算机实际存储的尾数位。js中的toString(2)由于尾数位只能存储52个数字,这就能解释toString(2)的执行结果了:如果计算机没有存储空间的限制,那么0.1的二进制应该是:0.00011001100110011001100110011001100110011001100110011001…科学计数法尾数位1.1001100110011001100110011001100110011001100110011001…但是由于限制,有效数字第53位及以后的数字是不能存储的,它遵循,如果是1就向前一位进1,如果是0就舍弃的原则。0.1的二进制科学计数法第53位是1,所以就有了下面的结果:0.00011001100110011001100110011001100110011001100110011010.2有着同样的问题,其实正是由于这样的存储,在这里有了精度丢失,导致了0.1+0.2!=0.3。事实上有着同样精度问题的计算还有很多,我们无法把他们都记下来,所以当程序中有数字计算时,我们最好用工具库来帮助我们解决,下面是两个推荐使用的开源库:number-precisionmathjs/下面我们再来看上面的其他两个问题。Why JavaScript计算出的 0.1的二进制 是这么多位而不是更多位???上面的toString原理帮我们解答了这个问题,在有效数字第53位以后的数字将遵循1进0舍的原则,内存中只允许存储52位有效数字。Why JavaScript计算的(0.1+0.2)的二进制和我们自己计算的(0.1+0.2)的二进制结果不一样呢???我们自己计算的0.1+0.2::0.0100110011001100110011001100110011001100110011001100111实际上这个结果的有效数字已经超过了52位,我们要从末尾进行1进0舍得到下面的结果0.0100110011001100110011001100110011001100110011001101JavaScript能表示的最大数字由与IEEE 754双精度64位规范的限制:指数位能表示的最大数字:1023(十进制)尾数位能表达的最大数字即尾数位都位1的情况所以JavaScript能表示的最大数字即位1.111…X 21023 这个结果转换成十进制是1.7976931348623157e+308,这个结果即为Number.MAX_VALUE。最大安全数字JavaScript中Number.MAX_SAFE_INTEGER表示最大安全数字,计算结果是9007199254740991,即在这个数范围内不会出现精度丢失(小数除外),这个数实际上是1.111…X 252。我们同样可以用一些开源库来处理大整数:node-bignumnode-bigint其实官方也考虑到了这个问题,bigInt类型在es10中被提出,现在Chrome中已经可以使用。bigInt类型BigInt 是第七种原始类型。BigInt 是一个任意精度的整数。这意味着变量现在可以计算9007199254740991即最大安全整数以上的数字。const b = 1n; // 追加 n 以创建 BigInt在过去,不支持大于 9007199254740992 的整数值。如果超过,该值将锁定为 MAX_SAFE_INTEGER + 1:const limit = Number.MAX_SAFE_INTEGER;⇨ 9007199254740991limit + 1;⇨ 9007199254740992limit + 2;⇨ 9007199254740992 <— MAX_SAFE_INTEGER + 1 exceededconst larger = 9007199254740991n;⇨ 9007199254740991nconst integer = BigInt(9007199254740991); // initialize with number⇨ 9007199254740991nconst same = BigInt(“9007199254740991”); // initialize with “string”⇨ 9007199254740991ntypeoftypeof 10;⇨ ’number’typeof 10n;⇨ ‘bigint’ ...

March 4, 2019 · 1 min · jiezi

PHP中的pack和unpack函数

转载请注明文章出处:https://tlanyan.me/php-pack-a…PHP有两个重要的冷门函数:pack和unpack。在网络编程,读写图像文件等场景,这两个函数几乎必不可少。鉴于文件读写/网络编程,或者说字节流处理的重要性,掌握这两个函数是迈向高级PHP编程的基础。本文先介绍字节和字符的区别,说明两个函数存在的必要性和重要性。然后介绍基本用法和使用场景,让读者对其有大体了解,为实际使用中奠定基础。字节和字符PHP的优势是简单易用,熟练运用 字符串 和 数组 相关函数就能抗住一般的需求。日常工作中多用到字符串,所以PHP开发对字符都比较熟悉,稍微资深点基本能也能弄清字符编码。但字符的伴生概念:字节,不少PHP开发并不知晓/熟悉。这不怪他们。PHP世界里极少出现“字节(流)”的概念:没有byte关键字(当然也没有char),官方文档也没提字节;没有原生的数组支持(常用的array其实是hashtable);当然字符串(string)能表达其他语言中的字节数组(Byte Array, byte[])。字节和字符有什么联系和区别呢?简单来说字节是计算机存储和操作的最小单位,字符是人们阅读的最小单位;字节是存储(物理)概念,字符是逻辑概念;字节代表数据(内涵和本质),字符代表其含义;字符由字节组成。举几个例子说明两者区别:“中国”包含2个字符,GBK编码表示需要4个字节,UTF-8编码需要6个字节;数字“1234567890”,包含10个字符,用int32类型表示只需4个字节;下面的图片占用42582个字节,用字符表示是“我老婆”,只占用3个字符:再举一个常用的例子说明字符和字节的区别。开发中我们常用md5算法获取数据的哈希值,算法返回一个128位(bit)的数据(16个字节)。为方便查看其值,人们约定成俗地用十六进制表示,结果就是我们熟知的32位长度的字符串(不区分大小写)。32长度字符串不是md5算法的必然结果,16字节数据才是其本质。如果你愿意,可以用一个小于2^128的数字表示哈希结果,也可以将16字节base64编码后作为其结果。所以常用的32位哈希值与md5返回的16字节关系为:一个是字符表示,另一个则是其本质(字符数组)(PHP的md5函数第二个参数值为true便可得到16字节数据,或hash函数第三个参数为true)。相关概念还有字节序、字符编码等,本文不做展开。感兴趣的读者可参考本人之前的博客“文件和字符编码”或相关材料。引言PHP中专门处理字符串的函数有几十个,加上正则、时间等函数,字符串处理的函数不下百个。相比之下字节处理门庭冷落,相关函数寥寥无几。除了常用的ord/chr,哈希加密函数返回的原始字节、openssl库的openssl_random_pseudo_bytes等函数真正处理或返回 字节外,最重要的两个字节处理函数是pack和unpack。本节从问题引出pack函数的使用。问题考虑一个简单的问题:宇宙的终极答案42在内存中是如何表示的(或者说怎么获取其字节数组)?因为42是一个整数,根据硬件不同,其占用字节大小可能为1, 2, 4, 8等。这里我们限定一个整数占用4个字节,于是问题的等价表述为:怎样将一个整数转换成字节数组(本机序,4个字节)?分析因为是多字节,所以要考虑字节序的问题。42不超过255,只占用一个字节,故而其他三个字节都是0。据此得到结论:如果是大端序(低位字节存放在地址高位),四个字节分别是:0 0 0 42;如果是小端序,结果则是:42 0 0 0。那怎么知道机器的字节序呢?PHP没有提供相关功能,也不能像C语言直接取地址访问字节数据。无所不能的PHP该怎么搞定字节序,或者说完成数据向字节的转换?方案PHP应用层面,数据向字节(数组)的转换是pack的专场,字节(数组)向数据的转换则是unpack的专场。除这两个函数,字节数组(或二进制数据)向数据的转换几无可能(如果有请不吝指教)。现在我们用pack函数获取42在内存中的字节数组。相关代码如下:function intToBytes(int $num) : string { return pack(“l”, $num);}function outputBytes(string $bytes) { echo “bytes: “; for ($i = 0; $i &lt; strlen($bytes); ++ $i) { echo ord($bytes[$i]), " “; } echo PHP_EOL;}outputBytes(intToBytes(42));// 程序输出:bytes: 42 0 0 0本人计算机用的英特尔的CPU,x86架构是小端序,所以程序输出符合预期。延伸一下,怎么判断机器的字节序?有了pack函数,答案非常简单:function bigEndian() : bool { $data = 0x1200; $bytes = pack(“s”, $data); return ord($bytes[0]) === 0x12;}调用函数便返回本机是否大端序。上述是pack函数简单的使用场景,接下来分别介绍pack和unpack函数。pack和unpackpack函数pack是“打包/封包”的意思。如其名,pack函数的工作是将数据按照格式打包成字节数组。函数原型为:pack ( string $format [, mixed $… ] ) : string形式上与printf系列函数相同:第一个参数是格式字符串,其余参数是要格式化的参数。不同之处在于pack函数的格式中不能出现元字符和量词外的其他字符,所以不需要%符号。上文的例子中使用了"l"和"s"两个格式化元字符,pack函数的元字符主要分为三类:字符串:a、A等;将数据转成字符串,功能上与sprintf类似,例如整数32转换成字符串"32”;字节:h和H;对字节进行16进制编码,区别在于低位还是高位在前,功能上与dechex等函数类似;char/short/int/long/float/double六种基本类型:c/s/i/l等;将数据转换成对应类型的字节数组,除char类型外(暂)没有其他函数可替代;注意:char和a/A等的区别是a/A等输入为字符(串),而’s/S’的输入要求是小于256的整数,输入字符会得到0。量词比较简单:数字和"“两种。例如"i2"表示将两个参数按照整数转换,“c"表示后续都按照char类型转换。unpackunpack是pack的反向操作:将字节数组解析成有意义的数据。其函数原型为:unpack ( string $format , string $data [, int $offset = 0 ] ) : arrayunpack函数需要注意的是第一个参数和返回值。返回值好理解,pack函数相当于将除格式化参数外的参数数组(想象成call_user_func_array的参数)变成一个字节数组;unpack做相反的事情:释放数据,得到输入时的参数数组。返回一个数组,其键分别是什么呢?这便是格式化参数($format)在pack和unpack的不同之处:unpack应该对释放出来的数据命名,用”/“分隔各组数据。由于格式化参数允许有非元字符和量词外的字符,为了区分数据,不同数据间的”/“分隔符必不可少。一个例子:$bytes = pack(“iaa*”, 42, “:”, “The answer to life, the universe and everything”);outputBytes($bytes);$result = unpack(“inumber/acolon/aword”, $bytes);print_r($result);// 程序输出:bytes: 42 0 0 0 58 84 104 101 32 97 110 115 119 101 114 32 116 111 32 108 105 102 101 44 32 116 104 101 32 117 110 105 118 101 114 115 101 32 97 110 100 32 101 118 101 114 121 116 104 105 110 103Array( [num] =&gt; 42 [colon] =&gt; : [word] =&gt; The answer to life, the universe and everything)如果不对释放出来的数据命名会怎么样?例如上例中unpack的格式化参数为:“i/a/a",结果是什么呢?其结果为:Array( [1] => The answer to life, the universe and everything)为何?官方文档上如是说:Caution If you do not name an element, numeric indices starting from 1 are used. Be aware that if you have more than one unnamed element, some data is overwritten because the numbering restarts from 1 for each element.翻译过来就是:如果你不对数据命名,默认的1, 2, 3…就用来当作键值。如果有多组数据,每组都用同样的下标,会导致数据覆盖。所以能理解 “i/a/a*” 为何只剩最后一组数据了吧?应用场景读取图像、word/excel文件,解析binlog、二进制ip数据库文件等场合,pack和unpack几乎必不可少。本文举例说一下pack和unpack在网络编程时协议解析的用途。假设我们的tcp包格式为:前四个字节表示包大小,其余字节为数据内容。于是客户(发送)端的send函数可以长这样:public function send($data) { // 这里假设$data已经做了序列化、加密等操作,是字节数组 // 计算报文长度,封装报文 $len = strlen($data); $header = pack(“L”, $len); // 转换成网络(大端)序 $header = xxx // 封包 $binary = $header . $data; // 调用fwrite/socket_send等将数据写入内核缓冲区 …}服务(接收)端根据协议解析接收到的数据流:public function decodable($session, $buffer) { $dataLen = strlen($buffer); // 非法数据包 if ($dataLen &lt; 4) { // 关闭连接、记录ip等 …. return NOT_OK; } // 获取前四个字节 $header = substr($buffer, 0, 4); // 转换成主机序 $header = xxx // 解析数据长度 $len = unpack(“L”, $header); // 单个报文不能超过8M,例如限制上传的图像大小 if ($len &gt; 8 * 1024 * 1024) { // 关闭连接等 return NOT_OK; } // 检查数据包是否满足协议要求 if ($dataLen - 4 &gt;= $len) { return OK; } // 数据未全部到达,继续等待 return NEED_DATA;}通过pack和unpack,我们顺利的处理报文协议和二进制字节流的发送和解析。如果你用\n作为报文分隔符,pack和unpack也许用不到。但在网络通讯中直接传递字符毕竟少数(相当于明文传送),大多数情况下的二进制数据流的解析还是要靠pack和unpack。总结除分配内存,最重要的系统调用莫过于文件读写和网络连接,而两者的本质操作对象都是字节流。pack和unpack为PHP提供了底层字节操作的能力,在二进制数据处理中十分有用。有志于跳出web编程的PHP开发应该都要掌握这两个函数。参考文件和字符编码PHP Manual: packPHP Manual: unpackHandling binary data in PHP with pack() and unpack()PHP: 深入pack/unpack ...

February 24, 2019 · 2 min · jiezi

JS的二进制操作

之前在某个项目中,遇到了许多JS的二进制操作场景,因此总结下JS中的二进制操作方法。所谓二进制操作,是指操作变量实际存储的值,比如获取字符A的Unicode值,或者将值100填入到8个字节中。1. 位操作符JS中的位操作与很多语言类似,具体的位运算符如下表所示。运算符用法描述按位与a & b对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。按位或a | b对于每一个比特位,当两个操作数相应的比特位至少有一个1时,结果为1,否则为0。按位异或a ^ b对于每一个比特位,当两个操作数相应的比特位有且只有一个1时,结果为1,否则为0。按位非~ a反转操作数的比特位,即0变成1,1变成0。左移a << b将 a 的二进制形式向左移 b (< 32) 比特位,右边用0填充。有符号右移a >> b将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位。无符号右移a >>> b将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。2. 字符和Unicode编码在介绍具体的方法前,我们需要先了解下UCS-2和UTF-16编码。UCS-2是一个16bit长度的编码集,它的表示范围是0到0xFFFF。UTF-16的表示范围是0到0x10FFFF,它由1个或者2个16bit的编码单元组成。其中UCS-2是UTF-16的子集,UTF-16编码在0到0x00FFFF的范围称为BMP(基本多文种平面),BMP与UCS-2的编码完全一致。更详细的说明可以参考这里。2.1 String.fromCharCodefromCharCode 方法返回指定的UCS-2编码对应的字符串。它是String上的静态方法,不可通过字符串对象直接访问。因为入参是UCS-2编码值,所以不能多于16bit,即入参值要小于65536。如果入参需要大于65536,可以使用 String.fromCodePoint 。String.fromCharCode(65) // AString.fromCharCode(65, 66, 68) // ABD2.2 String.prototype.charCodeAtcharCodeAt 返回字符串指定位置的字符的UTF-16编码。该方法可以直接从字符串对象进行调用。如果该字符不能使用一个UTF-16编码单元(16bit)来表示时,该方法只会返回第一个编码单元。如果需要获取完整的编码,可以使用 String.prototype.codePointAt 。“AB”.charCodeAt(0) // 65"AB".charCodeAt(1) // 663. ArrayBufferArrayBuffer用来表示原始的二进制数据缓存区,但是不可直接对ArrayBuffer进行操作,需要借助DataView或者类型数组对象来对缓存区的内容进行读写。3.1 DataViewDataView 可以理解为数据视窗,通过 DataView 对象可以对 ArrayBuffer 进行读写操作。const buffer = new ArrayBuffer(4); // 申请2个字节长度的缓存区const view1 = new DataView(buffer); // view1的范围是整个缓存区const view2 = new DataView(buffer, 2, 1) // view2的范围是从第2个字节开始往后的一个字节// 向一个16bit的内容中填入一个带符号的数// 参数的含义依次为 输入内容的位置、输入的值、是否使用小端方式(默认大端)view1.setInt16(0, 0x0A0B, false);view1.getInt8(0); // 10,即0x0Aview1.getInt8(1); // 11,即0x0Bview2.setUint8(0, 255);view2.getInt8(0); // 按照有符号数来读取,结果为-1更多的操作方法可以参考DataView。3.2 类型数组对象类型数组对象有很多种,比如Uint8Array, Int32Array等。将ArrayBuffer转化为类型数组后,就可以像数组一样来操作缓存区。const buffer = new ArrayBuffer(8);const arr1 = new Int16Array(buffer);const arr2 = new Uint8Array(buffer);arr1[0] = 256;arr2[7] = 255;console.log(arr1); // [256, 0, 0, 255]console.log(arr2); // [0, 1, 0, 0, 0, 0, 255, 0]参考文献按位操作符 - JavaScript | MDN[Unicode字符平面映射](之前在某个项目中,遇到了许多JS的二进制操作场景,因此总结下JS中的二进制操作方法。所谓二进制操作,是指操作变量实际存储的值,比如获取字符A的Unicode值,或者将值100填入到8个字节中。1. 位操作符JS中的位操作与很多语言类似,具体的位运算符如下表所示。运算符用法描述按位与a & b对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。按位或a | b对于每一个比特位,当两个操作数相应的比特位至少有一个1时,结果为1,否则为0。按位异或a ^ b对于每一个比特位,当两个操作数相应的比特位有且只有一个1时,结果为1,否则为0。按位非~ a反转操作数的比特位,即0变成1,1变成0。左移a << b将 a 的二进制形式向左移 b (< 32) 比特位,右边用0填充。有符号右移a >> b将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位。无符号右移a >>> b将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。2. 字符和Unicode编码在介绍具体的方法前,我们需要先了解下UCS-2和UTF-16编码。UCS-2是一个16bit长度的编码集,它的表示范围是0到0xFFFF。UTF-16的表示范围是0到0x10FFFF,它由1个或者2个16bit的编码单元组成。UCS-2是UTF-16的子集,当字符的编码范围在0x00FFFF。更详细的说明可以参考这里。2.1 String.fromCharCodefromCharCode 方法返回指定UCS-2编码对应的字符串。它是String上的静态方法,不可通过字符串对象直接访问。所以 fromCharCode 的入参值要小于65536。否则需要使用 String.fromCodePoint 代替。String.fromCharCode(65) // AString.fromCharCode(65, 66, 68) // ABD2.2 String.prototype.charCodeAtcharCodeAt 返回字符串指定位置的字符的UTF-16编码。该方法可以直接从字符串对象进行调用。如果该字符不能使用一个UTF-16编码单元(16bit)来表示时,该方法只会返回第一个编码单元。如果需要获取完整的编码,需要使用String.prototype.codePointAt代替。“AB”.charCodeAt(0) // 65"AB".charCodeAt(1) // 66PS:3. ArrayBufferArrayBuffer用来表示原始的二进制数据缓存区,类似于申请一块内存空间,但是不可直接对缓存区进行操作,需要借助DataView或者类型数组对象来对缓存区的内容进行读写。3.1 DataViewDataView 可以理解为数据视窗,通过 DataView 对象可以对 ArrayBuffer 进行读写操作。const buffer = new ArrayBuffer(4); // 申请2个字节长度的缓存区const view1 = new DataView(buffer); // view1的范围是整个缓存区const view2 = new DataView(buffer, 2, 1) // view2的范围是从第2个字节开始往后的一个字节// 向一个16bit的内容中填入一个带符号的数// 参数的含义依次为 输入内容的位置、输入的值、是否使用小端方式(默认大端)view1.setInt16(0, 0x0A0B, false);view1.getInt8(0); // 10,即0x0Aview1.getInt8(1); // 11,即0x0Bview2.setUint8(0, 255);view2.getInt8(0); // 按照有符号数来读取,结果为-1更多的操作方法可以参考DataView。3.2 类型数组对象类型数组对象有很多种,比如Uint8Array, Int32Array等。将ArrayBuffer转化为类型数组后,就可以像数组一样来操作缓存区。const buffer = new ArrayBuffer(8);const arr1 = new Int16Array(buffer);const arr2 = new Uint8Array(buffer);arr1[0] = 256;arr2[7] = 255;console.log(arr1); // [256, 0, 0, 255]console.log(arr2); // [0, 1, 0, 0, 0, 0, 255, 0]参考文献按位操作符 - JavaScript | MDNUnicode字符平面映射JavaScript’s internal character encoding: UCS-2 or UTF-16?String.fromCharCode() - JavaScript | MDNString.prototype.charCodeAt() - JavaScript | MDNArrayBuffer - JavaScript | MDN ...

February 15, 2019 · 2 min · jiezi

【剑指offer】9.二进制中1的个数

题目输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。分析这是一道考察二进制的题目二进制或运算符(or):符号为|,表示若两个二进制位都为0,则结果为0,否则为1。二进制与运算符(and):符号为&,表示若两个二进制位都为1,则结果为1,否则为0。二进制否运算符(not):符号为~,表示对一个二进制位取反。异或运算符(xor):符号为^,表示若两个二进制位不相同,则结果为1,否则为0左移运算符m << n 表示把m左移n位,左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0,比如:00001010<<2 = 00101000右移运算符m >> n 表示把m右移n位,右移n位的时候,最右边的n位将被丢弃,同时在最左边补上n个0,比如:00001010>>2 = 00000010我们可以让目标数字和一个数字做与运算这个用户比较的数字必须只有一位是1其他位是0,这样就可以知道目标数字的这一位是否为0。所以用于比较的这个数字初始值为1,比较完后让1左移1位,这样就可以依次比较所有位是否为1。代码function NumberOf1(n){ let flag = 1; let count = 0; while(flag){ if(flag & n){ count++; } flag = flag << 1; } return count;}

January 20, 2019 · 1 min · jiezi

leetcode讲解762.Prime Number of Set Bits in Binary Representation

题目Given two integers L and R, find the count of numbers in the range [L, R] (inclusive) having a prime number of set bits in their binary representation.(Recall that the number of set bits an integer has is the number of 1s present when written in binary. For example, 21 written in binary is 10101 which has 3 set bits. Also, 1 is not a prime.)Example 1:Input: L = 6, R = 10Output: 4Explanation:6 -> 110 (2 set bits, 2 is prime)7 -> 111 (3 set bits, 3 is prime)9 -> 1001 (2 set bits , 2 is prime)10->1010 (2 set bits , 2 is prime)Example 2:Input: L = 10, R = 15Output: 5Explanation:10 -> 1010 (2 set bits, 2 is prime)11 -> 1011 (3 set bits, 3 is prime)12 -> 1100 (2 set bits, 2 is prime)13 -> 1101 (3 set bits, 3 is prime)14 -> 1110 (3 set bits, 3 is prime)15 -> 1111 (4 set bits, 4 is not prime)Note:L, R will be integers L <= R in the range [1, 10^6].R - L will be at most 10000.题目地址讲解这一题同样是针对二进制表示进行出题,题目的意思是计算一个数的二进制表示中有多少个1,如果是素数个1,结果就加一。性能的提升点应该是在 isPrime 函数的实现上。我采用的是最普通的方法,从2到$\sqrt x$进行累加,判断x是否是素数,效率比较低。java代码class Solution { public int countPrimeSetBits(int L, int R) { int result = 0; for(int i=L;i<=R;i++){ int temp = i; int count = 0; while(temp>0){ if(temp%2==1){ count++; } temp >>= 1; } if(isPrime(count)){ result++; } } return result; } private boolean isPrime(int a){ if(a<=1){ return false; } for(int i=2;i<=Math.sqrt(a);i++){ if(a%i==0){ return false; } } return true; }} ...

January 7, 2019 · 2 min · jiezi

leetcode讲解--693. Binary Number with Alternating Bits

题目Given a positive integer, check whether it has alternating bits: namely, if two adjacent bits will always have different values.Example 1:Input: 5Output: TrueExplanation:The binary representation of 5 is: 101Example 2:Input: 7Output: FalseExplanation:The binary representation of 7 is: 111.Example 3:Input: 11Output: FalseExplanation:The binary representation of 11 is: 1011.Example 4:Input: 10Output: TrueExplanation:The binary representation of 10 is: 1010.题目地址讲解这一题的意思是判断一个整数的二进制形式,是否是0和1互相间隔排列的。题目挺简单的,但我想做的是空间O(1)。所以我用两个变量来存储遍历过程中需要保存的前一位和后一位,然后设置一个首次访问flag。遍历一遍二进制,结果就出来了。java代码class Solution { public boolean hasAlternatingBits(int n) { if(n<=2){ return true; } int temp = n; int now = 0; int previous = 0; boolean flag = true; while(temp>0){ if(flag){ now = temp%2; temp >>= 1; flag = false; }else{ previous = now; now = temp%2; if(previous==now){ return false; } temp >>= 1; } } return true; }} ...

January 7, 2019 · 1 min · jiezi