精华内容
下载资源
问答
  • 主要介绍了Python使用base64模块进行二进制数据编码详解,具有一定借鉴价值,需要的朋友可以参考下
  • 在Rust中使用二进制数据和POD结构的工具的集合。 是一种构建安全接口以将POD结构与字节片进行来回转换的方法。 以安全,稳定的方式公开未对齐的打包数据结构。 提供了pod帮助程序,以及用于处理二进制数据流的...
  • 【转】HTTP传输二进制 数据编码解码

    千次阅读 2017-07-03 18:41:04
    【转】HTTP传输二进制初探 从第一次接触http协议的时候,不知是怎么回事,形成了这么一个错误的观点,认为http协议是个纯ASCII字符协议,也就是说在http流里是看不到二进制流的0x00值的。其实答案是:http协议...

    【转】HTTP传输二进制初探

    从第一次接触http协议的时候,不知是怎么回事,形成了这么一个错误的观点,认为http协议是个纯ASCII字符协议,也就是说在http流里是看不到二进制流的0x00值的。其实答案是:http协议里的content可以是纯二进制流。
     
    http://my.chinaunix.net/space.php?uid=22568683&do=blog&id=84701

    关于HTTP传输ASCII文本内容的过程相信大家都应该容易理解,因为HTTP请求头和响应头都是以ASCII文本方式传输的。而对于HTTP传输二进制流的相关细节,其实没有我想象中的那么复杂,以前学习POP3和SMTP(这两个都是邮件传输协议)的时候,知道他们都只能传输ASCII文本,如果要在邮件中加入附件,如一张图片(图片文件就是二进制文件)那就得先对图片文件转码,即将邮件协议不能传输的二进制数据流转换成可被邮件协议传输的ASCII数据流,其中用的最多的转换就是BASE64编码转换。其实BASE64编码转换也同样适合于HTTP协议,只有你在转换后将HTTP响应头中的Transfer-Encoding设置为base64,当然如果客服端浏览器不支持base64编码那这种转换也是徒劳的,不过幸好现在几乎所有浏览器都支持BASE64(你能用浏览器查看邮件中的附件就是证据)。

    不过话又回来,既然HTTP能够直接支持二进制的数据流传输,那我们又何必绕着弯子,走冤枉路呢?

    如果你和一样,也对HTTP能直接传输二进制感到疑问,那么下面的内容会很对你胃口。

     我们以一张图片的传输来说明这个问题:

     http://gimg.baidu.com/img/gs.gif    这是 百度主页上一张非常小的图片的链接地址 即右侧
    图片  
    我们用到的工具有:

    Firefox  浏览器

    Firebug  一个非常不错的 web调试器,Firefox插件

    Ethereal  网络抓包工具

     如果你对上面三个工具不是很了解,建议你先去google一下。然后再来阅读。

     下面是我们在Firefox地址栏里面输入http://gimg.baidu.com/img/gs.gif  回车后,Firefox默默地为我们做的事情。关于HTTP通行的细节请参阅我以前的 文章

    http://blog.chinaunix.net/u3/104217/showart.php?id=2075210

    http://p.99081.com/unix/http_protocol_summary.html

     

    GET  /gs.gif  HTTP/1.1

    Host: gimg.baidu.com

    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

    Accept-Language: zh-cn,zh;q=0.5

    Accept-Encoding: gzip,deflate

    Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7

    Keep-Alive: 300

    Connection: keep-alive

     当Firefox发出该请求后,服务器接收并分析该请求,经分析后得出客服端浏览器请求的文件(或者说页面)为/img/gs.gif (第一个斜杆代表服务器根目录),于是服务器(百度的HTTP服务器为Apache)从服务器主机的硬盘(或者直接从内存的缓冲区)中读出该图片(注意哦,直接读二进制流),并将其拼接到HTTP响应头后,然后把这片数据(我指的是HTTP响应头加上图片的二进制数据)拷贝到TCP的发送缓冲区(也就是调用send函数)。

    下面即位客服端收到的从服务器端发来的数据流:

     


    我们还是先来分析一下响应头

     HTTP/1.1 200 OK

    Date: Tue, 27 Oct 2009 13:43:15 GMT

    Server: Apache

    Last-Modified: Fri, 11 Aug 2006 04:20:15 GMT

    Accept-Ranges: bytes

    Content-Length: 91

    Cache-Control: max-age=315360000

    Expires: Fri, 25 Oct 2019 13:43:15 GMT

    Connection: close

    Content-Type: image/gif

     注意最后一个字段 Content-Type:image/gif 这说明传输的是一个image对象,该对象为gif格式。另外我们还有记下Content-length:91 这说明传输的数据(即gs.gif图片)的大小为91个字节,此外我们还发现响应头中并没有Transfer-Encoding这个字段,这说明传输的数据没有经过任何形式的编码转换,传输的就是源文件的内容。

    请认真查看上图中蓝底部分,在蓝底的最后那一行,有两个连续的 0d 0a 0d 0a ,这说明HTTP响应头已经结束,接下来的内容为传输的文件。好啦,那接下来当然是要分析传输的文件到底是啥东西了。请看下图

     

    图中蓝底的部分即为传输的数据流(即传输的文件),这些是什么东西,我也搞不懂,(估计只有搞图片压缩算法的人能够看得懂),不过没关系,我们可以先把图片保存在本地,然后用一个十六进制查看软件打开该图片,即可知道其中的奥秘。

     

    下面是用UltraEdit打开该图片后的截图

     

    请比较一下上面两张图片是不是有很多相同的地方呀,其实上面两张图中,第一张中的蓝底部分就是第二张中的数据。这下你应该明白了吧,其实HTTP传输的就是图片文件的二进制编码,Apache没有对二进制文件进行任何形式的编码转换。我们还可以计算一下这个图片的大小:16 * 5 + 11 = 91 (也就是 0x5a – 0x00 = 0x5a),正好和HTTP响应头中的Content-Length相等。

     如果没有UltraEdit等十六进制编辑器,我写了个简单的程序以供查看,下面是源码:

    check_hex.c


    http://blog.csdn.net/chen1403876161/article/details/51546653

     http://blog.csdn.net/acmdream/article/details/71334169


    #include <stdio.h>  
    #include <string.h>  
    long int encode( char *src,long int src_len, char *dst)  
           
    {  
               
        long int i = 0, j = 0;  
               
        char base64_map[65] = "BADCFEGHIJKLMNOPQRSTUVWXYZbadcfeghijklmnopqrstuvwxyz0123456789+/";  
               
        for (; i < src_len - src_len % 3; i += 3) {  
               
            dst[j++] = base64_map[(src[i] >> 2) & 0x3F];  
               
            dst[j++] = base64_map[((src[i] << 4) & 0x30) + ((src[i + 1] >> 4) & 0xF)];  
           
            dst[j++] = base64_map[((src[i + 1] << 2) & 0x3C) + ((src[i + 2] >> 6) & 0x3)];  
           
            dst[j++] = base64_map[src[i + 2] & 0x3F];  
        }  
               
        if (src_len % 3 == 1) {  
               
            dst[j++] = base64_map[(src[i] >> 2) & 0x3F];  
               
            dst[j++] = base64_map[(src[i] << 4) & 0x30];  
               
            dst[j++] = '=';  
               
            dst[j++] = '=';  
               
        }else if (src_len % 3 == 2) {  
               
            dst[j++] = base64_map[(src[i] >> 2) & 0x3F];  
               
            dst[j++] = base64_map[((src[i] << 4) & 0x30) + ((src[i + 1] >> 4) & 0xF)];  
               
            dst[j++] = base64_map[(src[i + 1] << 2) & 0x3C];  
               
            dst[j++] = '=';  
               
        }  
           
        dst[j] = '/0';  
        printf("encode length:%ld/n",j);  
        return j;  
               
    }   
    long int decode(char *src, long int src_len, char *dst)  
       
    {  
           
        long    int i = 0, j = 0;  
           
        unsigned char base64_decode_map[256] = {  
               
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,  
                   
                255, 0, 255, 255, 255, 1, 0, 3, 2, 5, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14,  
                   
                15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 27, 26, 29,  
                   
                28, 31, 30, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,  
                   
                49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  
                   
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};  
               
               
           
        for (; i < src_len; i += 4) {  
               
            dst[j++] = base64_decode_map[src[i]] << 2 |  
                   
                base64_decode_map[src[i + 1]] >> 4;  
               
            dst[j++] = base64_decode_map[src[i + 1]] << 4 |  
                   
                base64_decode_map[src[i + 2]] >> 2;  
               
            dst[j++] = base64_decode_map[src[i + 2]] << 6 |  
                   
                base64_decode_map[src[i + 3]];  
               
        }  
        dst[j] = '/0';  
        printf("decode length :%ld/n",j);  
        return j;  
                   
    }  
    int main(){  
        int num = 11111;  
        printf("int:%ld/n",sizeof(int));  
        char num_char[100];  
        char num_char_b64[200];  
        memset(num_char,'/0',100);  
        memcpy(num_char,&num,4);  
        printf("content_to_be_encoded:%s/n",num_char);  
        int len = encode(num_char,4,num_char_b64);  
        printf("content_encoded:%s/n",num_char_b64);  
        int len2= decode(num_char_b64,len,num_char);  
        printf("%s/n",num_char);  
        memcpy(&num,num_char,4);  
        printf("int:%d/n",num);  
    }
    我的游戏中心服务端的HTTP server,客户端请求采用了表单Form的形式提交数据,服务端利用Boost解析字符数据,然后保存到数据库,再利用protoBuf打包数据,发送二进制数据内容的响应给客户端处理。实现了自定义数据格式和数据流的传输。


    展开全文
  • 二进制霍夫曼编码

    千次阅读 2019-02-22 15:26:02
    通过查看其他博客的内容,整理一篇关于二进制霍夫曼编码的笔记供大家参考...霍夫曼编码(Huffman Coding)是一种编码方式,是一种用于无损数据压缩的熵编码(权编码)算法,通常用于无损数据压缩。(详见维基百科:...

    通过查看其他博客的内容,整理一篇关于二进制霍夫曼编码的笔记供大家参考和讨论,如果有错误,欢迎大家联系我批评指正。

     

    一、二进制霍夫曼的原理

    我们可以将二进制霍夫曼编码拆分理解:

    二进制即 0、1;

    二进制编码就是用0和1的组合来表示其他字符;

    霍夫曼编码(Huffman Coding)是一种编码方式,是一种用于无损数据压缩的熵编码(权编码)算法,通常用于无损数据压缩。(详见维基百科:https://en.wikipedia.org/wiki/Huffman_coding#n-ary_Huffman_coding

     

    二、编码方法:

    1.将信源中n个符号按概率分布的大小,以递减次序排列(降序排列);

    2.用0和1分别分配给概率最小的两个信源符号,并将这两个概率最小的信源符号合并为一个新符号,并用这两个概率最小的信源符号合并成一个新符号,从而得到只包含n-1个符号的新信源,称其为缩减信源

    3.把缩减信源的符号仍按概率大小降序排列,再将最后两个概率最小的符号合并为一个新符号,并分别用0、1表示,这样又形成一个新的缩减信源;

    4.按2、3步骤继续进行直到缩减信源最后只剩下两个符号位置。再将最后两个新符号分别用0和1表示。最后这两个符号的概率之和为1,然后从最后一级缩减信源开始,依编码路径右后向前返回,就可以得到各信源符号所对应的码符号序列,即对应的码字。

     (参考:https://wenku.baidu.com/view/1aeb40145b8102d276a20029bd64783e09127de5.html

     

    三、例题说明。

    例题一:字符串“alibaba”的二进制霍夫曼编码有多少位?

    1.1计算符号的概率并进行降序排序。

    符号概率
    a3/7
    b2/7
    i1/7
    l1/7

    1.2构建二叉树 

                                                          

     

    1.3从根节点数,得到a、b、i、l 对应的码字。

    符号概率编码
    a3/70
    b2/711
    i1/7100
    l1/7101

     

    例题二:现有一段文言文,要通过二进制哈夫曼编码进行压缩。假设这段文言文只由4个汉字“之”“乎”“者”“也”组成,它们出现的次数分别为700、600、300、200。那么,“也”字的编码长度是?

    同理,按照例题一的三个步骤进行就可以得到:

    符号概率编码
    7000
    60011
    300101
    200100

                                                          

    答:3位。 

    这道“之乎者也”没有像例题一一样出现相同的概率,但是其实保证字符的码字具有唯一性就行了。

     

    例题三:字符串“ABCDADA”的二进制哈夫曼编码有多少位?

    符号概率编码
    A3/70
    D2/710
    C1/7111
    B1/7110

                                                                   

    所以字符串“ABCDADA”的二进制哈夫曼编码为(0110111100100),共13位。

     

    出现次数越多的字符,编码越短;出现次数越少的字符,编码越长。这样就能让编码后的文件大小能够最短。

    展开全文
  • 二进制的三种编码:原码,反码,补码 以前不是很理解,最近有时间进行了补充学习,通过两篇渐进关系的文章让我清晰了很多: 第一篇: 一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值...

    二进制的三种编码:原码,反码,补码

    以前不是很理解,最近有时间进行了补充学习,通过两篇渐进关系的文章让我清晰了很多:

    第一篇:

    一. 机器数和真值

    在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念.

    1、机器数

    一个数在计算机中的二进制表示形式,  叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.

    比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。

    那么,这里的 00000011 和 10000011 就是机器数。

    2、真值

    因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。

    例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

     

    二. 原码, 反码, 补码的基础概念和计算方法.

    在探求为何机器要使用补码之前, 让我们先了解原码, 反码和补码的概念.对于一个数, 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式.

    1. 原码

    原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

    [+1] = 0000 0001

    [-1] = 1000 0001

    第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:

    [1111 1111 , 0111 1111]

    [-127 , 127]

    原码是人脑最容易理解和计算的表示方式.

    2. 反码

    反码的表示方法是:

    正数的反码是其本身

    负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

    [+1] = [00000001] = [00000001]

    [-1] = [10000001] = [11111110]

    可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

    3. 补码

    补码的表示方法是:

    正数的补码就是其本身

    负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

    [+1] = [00000001] = [00000001] = [00000001]

    [-1] = [10000001] = [11111110] = [11111111]

    对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.

     

    三. 为何要使用原码, 反码和补码

    在开始深入学习前, 我的学习建议是先"死记硬背"上面的原码, 反码和补码的表示方式以及计算方法.

    现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:

    [+1] = [00000001] = [00000001] = [00000001]

    所以不需要过多解释. 但是对于负数:

    [-1] = [10000001] = [11111110] = [11111111]

    可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?

    首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.

    于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:

    计算十进制的表达式: 1-1=0

    1 - 1 = 1 + (-1) = [00000001] + [10000001] = [10000010] = -2

    如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.

    为了解决原码做减法的问题, 出现了反码:

    计算十进制的表达式: 1-1=0

    1 - 1 = 1 + (-1) = [0000 0001] + [1000 0001]= [0000 0001] + [1111 1110] = [1111 1111] = [1000 0000] = -0

    发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]和[1000 0000]两个编码表示0.

    于是补码的出现, 解决了0的符号以及两个编码的问题:

    1-1 = 1 + (-1) = [0000 0001] + [1000 0001] = [0000 0001] + [1111 1111] = [0000 0000]=[0000 0000]

    这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:

    (-1) + (-127) = [1000 0001] + [1111 1111] = [1111 1111] + [1000 0001] = [1000 0000]

    -1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000] 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000], 这是不正确的)

    使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].

    因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.

     

    四 原码, 反码, 补码 再深入

    计算机巧妙地把符号位参与运算, 并且将减法变成了加法, 背后蕴含了怎样的数学原理呢?

    将钟表想象成是一个1位的12进制数. 如果当前时间是6点, 我希望将时间设置成4点, 需要怎么做呢?我们可以:

    1. 往回拨2个小时: 6 - 2 = 4

    2. 往前拨10个小时: (6 + 10) mod 12 = 4

    3. 往前拨10+12=22个小时: (6+22) mod 12 =4

    2,3方法中的mod是指取模操作, 16 mod 12 =4 即用16除以12后的余数是4.

    所以钟表往回拨(减法)的结果可以用往前拨(加法)替代!

    现在的焦点就落在了如何用一个正数, 来替代一个负数. 上面的例子我们能感觉出来一些端倪, 发现一些规律. 但是数学是严谨的. 不能靠感觉.

    首先介绍一个数学中相关的概念: 同余

     

    同余的概念

    两个整数a,b,若它们除以整数m所得的余数相等,则称a,b对于模m同余

    记作 a ≡ b (mod m)

    读作 a 与 b 关于模 m 同余。

    举例说明:

    4 mod 12 = 4

    16 mod 12 = 4

    28 mod 12 = 4

    所以4, 16, 28关于模 12 同余.

     

    负数取模

    正数进行mod运算是很简单的. 但是负数呢?

    下面是关于mod运算的数学定义:

    clip_image001

    上面是截图, "取下界"符号找不到如何输入(word中粘贴过来后乱码). 下面是使用"L"和"J"替换上图的"取下界"符号:

    x mod y = x - y L x / y J

    上面公式的意思是:

    x mod y等于 x 减去 y 乘上 x与y的商的下界.

    以 -3 mod 2 举例:

    -3 mod 2

    = -3 - 2xL -3/2 J

    = -3 - 2xL-1.5J

    = -3 - 2x(-2)

    = -3 + 4 = 1

    所以:

    (-2) mod 12 = 12-2=10

    (-4) mod 12 = 12-4 = 8

    (-5) mod 12 = 12 - 5 = 7

     

    开始证明

    再回到时钟的问题上:

    回拨2小时 = 前拨10小时

    回拨4小时 = 前拨8小时

    回拨5小时= 前拨7小时

    注意, 这里发现的规律!

    结合上面学到的同余的概念.实际上:

    (-2) mod 12 = 10

    10 mod 12 = 10

    -2与10是同余的.

    (-4) mod 12 = 8

    8 mod 12 = 8

    -4与8是同余的.

    距离成功越来越近了. 要实现用正数替代负数, 只需要运用同余数的两个定理:

    反身性:

    a ≡ a (mod m)

    这个定理是很显而易见的.

    线性运算定理:

    如果a ≡ b (mod m),c ≡ d (mod m) 那么:

    (1)a ± c ≡ b ± d (mod m)

    (2)a * c ≡ b * d (mod m)

    如果想看这个定理的证明, 请看:http://baike.baidu.com/view/79282.htm

    所以:

    7 ≡ 7 (mod 12)

    (-2) ≡ 10 (mod 12)

    7 -2 ≡ 7 + 10 (mod 12)

    现在我们为一个负数, 找到了它的正数同余数. 但是并不是7-2 = 7+10, 而是 7 -2 ≡ 7 + 10 (mod 12) , 即计算结果的余数相等.

    接下来回到二进制的问题上, 看一下: 2-1=1的问题.

    2-1=2+(-1) = [0000 0010] + [1000 0001]= [0000 0010] + [1111 1110]

    先到这一步, -1的反码表示是1111 1110. 如果这里将[1111 1110]认为是原码, 则[1111 1110]原 = -126, 这里将符号位除去, 即认为是126.

    发现有如下规律:

    (-1) mod 127 = 126

    126 mod 127 = 126

    即:

    (-1) ≡ 126 (mod 127)

    2-1 ≡ 2+126 (mod 127)

    2-1 与 2+126的余数结果是相同的! 而这个余数, 正式我们的期望的计算结果: 2-1=1

    所以说一个数的反码, 实际上是这个数对于一个膜的同余数. 而这个膜并不是我们的二进制, 而是所能表示的最大值! 这就和钟表一样, 转了一圈后总能找到在可表示范围内的一个正确的数值!

    而2+126很显然相当于钟表转过了一轮, 而因为符号位是参与计算的, 正好和溢出的最高位形成正确的运算结果.

    既然反码可以将减法变成加法, 那么现在计算机使用的补码呢? 为什么在反码的基础上加1, 还能得到正确的结果?

    2-1=2+(-1) = [0000 0010] + [1000 0001] = [0000 0010] + [1111 1111]

    如果把[1111 1111]当成原码, 去除符号位, 则:

    [0111 1111] = 127

    其实, 在反码的基础上+1, 只是相当于增加了膜的值:

    (-1) mod 128 = 127

    127 mod 128 = 127

    2-1 ≡ 2+127 (mod 128)

    此时, 表盘相当于每128个刻度转一轮. 所以用补码表示的运算结果最小值和最大值应该是[-128, 128].

    但是由于0的特殊情况, 没有办法表示128, 所以补码的取值范围是[-128, 127]

    本人一直不善于数学, 所以如果文中有不对的地方请大家多多包含, 多多指点!


    第二篇(基于第一篇):

    转换:

    Java整型数据类型有:byte、char、short、int、long。要把它们转换成二进制的原码形式,必须明白他们各占几个字节。,一个字节==8位数

     数据类型                           所占位数
          byte                                       8 
          boolean                                8
          short                                    16
          int                                         32 
          long                                      64 
          float                                      32 
          double                                  64 
          char                                     16

    byte
    正数最大位0111 1111,也就是数字127 
    负数最大为1111 1111,也就是数字-128
    反码与补码
    1、反码:
            一个数如果是正,则它的反码与原码相同;
            一个数如果是负,则符号位为1,其余各位是对原码取反;

    2、补码:利用溢出,我们可以将减法变成加法
           对于十进制数,从9得到5可用减法:
           9-4=5    因为4+6=10,我们可以将6作为4的补数
           改写为加法:
           9+6=15(去掉高位1,也就是减10)得到5.

           对于十六进制数,从c到5可用减法:
           c-7=5    因为7+9=16 将9作为7的补数
           改写为加法:
           c+9=15(去掉高位1,也就是减16)得到5.

        在计算机中,如果我们用1个字节表示一个数,一个字节有8位,超过8位就进1,在内存中情况为(100000000),进位1被丢弃。

        ⑴一个数为正,则它的原码、反码、补码相同
        ⑵一个数为负,刚符号位为1,其余各位是对原码取反,然后整个数加1
         详细请参考http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
    Integer.toHexString的参数是int,如果不进行&0xff,那么当一个byte会转换成int时,由于int是32位,而byte只有8位这时会进行补位,
    例如补码11111111的十进制数为-1转换为int时变为11111111111111111111111111111111好多1啊,即0xffffffff但是这个数是不对的,这种补位就会造成误差。
    和0xff相与后,高24比特就会被清0了,结果就对了。

    还需要明白一点的是:计算机表示数字正负不是用+ -加减号来表示,而是用最高位数字来表示,0表示正,1表示负

    在计算机系统中,数值一律用补码来表示(存储)。
    主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补
    码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
    补码与原码的转换过程几乎是相同的。
    数值的补码表示也分两种情况:
    (1)正数的补码:与原码相同。
          例如,+9的补码是00001001。
    (2)负数的补码:符号位(最高位)为1,其余位为该数绝对值的原码按位取反;然后整个数加1。
          例如,-7的补码:因为是负数,则符号位为“1”,整个为10000111;其余7位为-7的绝对值+7的原码
         0000111按位取反为1111000;再加1,所以-7的补码是11111001。
    已知一个数的补码,求原码的操作分两种情况:
    (1)如果补码的符号位为“0”,表示是一个正数,所以补码就是该数的原码。
    (2)如果补码的符号位为“1”,表示是一个负数,求原码的操作可以是:符号位为1,其余各位取反,然后再整个数加1。
         例如,已知一个补码为11111001,则原码是10000111(-7):因为符号位为“1”,表示是一个负数,所以该位不变,仍为“1”;其余7位1111001取反后为0000110;再加1,所以是10000111。

    源码:是什么就是什么。负数就是最前面符号位为1。
    反码:正的就是补码,负的就是各位取反,0换1,1换0,注意,最高位符号为不变。
    补码:正的就是源码,负的就是反码+1
    比如: -1 -2
    以8位二进制为例
    源码:10000001 10000010
    反码:11111110 11111101
    补码:11111111 11111110
    补码这样做的好处是什么呢?
    请看-1+(-2)电脑怎么做:
    用源码:10000001 + (10000010)=00000011 这是什么?是-3吗?不是,是3。所以不能直接用源码做加法。
    用反码:11111110 + (11111101)=11111011 这是什么?是反码的"-4"
    用补码:11111111 + (11111110)=11111101 末尾减一再取反得10000011,所以结果是补码的-3。
    反码为什么出错?以4位数为例,高位为符号位(括号内为绝对值):
    1010 (2)取反 1101 (5)
    1011 (3)取反 1100 (4)
    然后 -2 + (-3) 变成了 -(5 + 4)超出8的部分舍去,得 1001,再取反得 1110,成了-6
    究其原因:各位取反的两数相加:1010+0101=1111必是全1即绝对值为7,2->5,3->4,相对于8共偏差了2,然后9=1mod8,1->6,只修正了1点偏差,
    结果就出现了1的偏差。补码中末尾加一就是修正了该偏差,得到正确的结果。即2->6,3->5.相对于8无偏差11=3mod8,3->5。

    位运算符:

    位移进制运算

    带符号右移 题:-15 >> 2 = -4

    15原码:   00000000 00000000 00000000 00001111 //32位,二进制
    反码:    11111111 11111111 11111111 11110000 //0变1,1变0
    补码:    11111111 11111111 11111111 11110001 //最后位加1,-15二进制
    右移2位:  11111111 11111111 11111111 11111100 //右边丢弃2位,前面30位保留,左边补1
    取反:      00000000 00000000 00000000 00000011 //0变1,1变0
    +1:                                       3+1
    结果:                                     =-4 //负号保留,十进制

    带符号左移 题: 10 << 2 = 40
    10 补码:    00000000 00000000 00000000 00001010 //32位,二进制
    左移2位:    00000000 00000000 00000000 00101000 //左边丢弃2位,右边补0
    结果:                                       40 //十进制
     

    无符号右移 题:-4321 >>> 30 = 3
    4321原码:         0000000000000000 00010000 11100011 //32位,二进制
    反码:           11111111 11111111 11101111 00011100 //0变1,1变0
    补码:           11111111 11111111 11101111 00011101 //最后位加1,-4321二进制
    无符号右移30位:  00000000 00000000 00000000 00000011 //右边丢弃30位,前面二位保留,左边补0
    结果:                                                3 //十进制


    & 位逻辑与 题:44 & 21 = 4
    44 补码:    00000000 00000000 00000000 00101100 //32位,二进制
    21 补码:    00000000 00000000 00000000 00010101 //32位,二进制
    & 运算:     00000000 00000000 00000000 00000100 //对应的两个二进制位均为1时 结果位才为1 否则为0
    结果:                                         4 //十进制   
                                   
    | 位逻辑与 题:9 | 5 = 13
    9 补码:    00000000 00000000 00000000 00001001 //32位,二进制
    5 补码:    00000000 00000000 00000000 00000101 //32位,二进制
    | 运算:    00000000 00000000 00000000 00001101 //对应的二个二进制位有一个为1时,结果位就为1
    结果:                                       13 //十进制

    ^ 位逻辑异或 题: 9 ^ 5 = 12
    9 补码:    00000000 00000000 00000000 00001001 //32位,二进制
    5 补码:    00000000 00000000 00000000 00000101 //32位,二进制
    | 运算:    00000000 00000000 00000000 00001100 //对应的二进制位相异时,结果为1
    结果:                                       12 //十进制

    ~ 位逻辑反 题: ~9 = -10
    9 补码:    00000000 00000000 00000000 00001001 //32位,二进制
    ~ 运算:    11111111 11111111 11111111 11110110 //最高位为1表示为一个负数,则进行取反加1
    取反:      00000000 00000000 00000000 00001001 //32位,二进制
    +1:                                      9+1 //32位,二进制
    结果:                                      -10 //十进制

    由于数据类型所占字节是有限的,而位移的大小却可以任意大小,所以可能存在位移后超过了该数据类型的表示范围,于是有了这样的规定: 如果为int数据类型,且位移位数大于32位,则首先把位移位数对32取模,不然位移超过总位数没意义的。所以4>>32与4>>0是等价的。

    如果为long类型,且位移位数大于64位,则首先把位移位数对64取模,若没超过64位则不用对位数取模。

    如果为byte、char、short,则会首先将他们扩充到32位,然后的规则就按照int类型来处理。

     

    实际应用:


    1.  判断int型变量a是奇数还是偶数    
         a&1  = 0 偶数 
         a&1 =  1 奇数 
    2.  求平均值,比如有两个int类型变量x、y,首先要求x+y的和,再除以2,但是有可能x+y的结果会超过int的最大表示范围,所以位运算就派上用场啦。
          (x&y)+((x^y)>>1); 
    3.  对于一个大于0的整数,判断它是不是2的几次方
        ((x&(x-1))==0)&&(x!=0); 
    4.  比如有两个int类型变量x、y,要求两者数字交换,位运算的实现方法:性能绝对高效
        x ^= y; 
        y ^= x; 
        x ^= y; 
    5. 求绝对值
        int abs( int x ) 
       { 
         int y ; 
         y = x >> 31 ; 
        return (x^y)-y ;        //or: (x+y)^y 
       }
    6.  取模运算,采用位运算实现:
         a % (2^n) 等价于 a & (2^n - 1) 
    7.  乘法运算   采用位运算实现
         a * (2^n) 等价于 a << n
    8.   除法运算转化成位运算
          a / (2^n) 等价于 a>> n 
    9.   求相反数
          (~x+1) 
    10  a % 2 等价于 a & 1


    展开全文
  • base64模块提供了Base64,Base32,Base16,Base85和Ascii85的编码解码

    base64模块

    前言

    昨天团队的学妹来问关于POP3协议的问题,所以今天稍稍研究了下POP3协议的格式和Python里面的poplib。而POP服务器往回传的数据里有一部分需要用到Base64进行解码,所以就顺便看了下Python里面的base64模块。

    本篇先讲一下base64模块,该模块提供了关于Base16,Base32,Base64,Base85和Ascii85的编码和解码相关的函数。有关poplib模块的内容,会在后面发上来。嗯,又挖了一个坑,这辈子挖的坑填不完了...

    以下内容摘自http://bbs.chinaunix.net/thread-1150250-1-1.html,详细说明了为什么回传的数据会先经过Base64编码:

    由於歷史原因,Internet上有些郵件系統只支援7Bit的字元傳輸,而漢字的內碼是8Bit的,當在電子郵件中發送中文時,如果經過這些只支援7Bit字元的郵件系統,便會將漢字內碼的第八位元的1全部變成0。
    以”中文”兩字為例,HEX為A4A4A4E5,當最高位元被清掉時就會變成24242465,也就是”$$$e”。telnet也存在這樣子的問題。
    除了中文郵件外,使用電子郵件傳送圖片、程式、壓縮文件等也會發生這個問題。所以在電子郵件中一般採用各種郵件編碼方式來解決這個問題,將8Bit按照一定的規則進行編碼,便可以完好地通過只支持7Bit字元的郵件系統。
    常見的郵件編碼有UU與MIME,而MIME(Multipurpose Internet Mail Extentions)一般翻譯成「多媒體傳送模式」,顧名思義,它標榜的就是可以傳送多媒體型式的檔案,可以在一封mail中附加各種型式檔案一起送出。
    MIME定義兩種編碼方法:Base64與QP(Quote-Printable),兩者使用時機不同,QP的規則是對於資料中的7bits無須重複encode,僅8bits資料轉成7bits。QP編碼適用於非US-ASCII的文字內容,例如我們的中文檔案,而Base64的編碼規則,是將整個檔案重新編碼,編成7bits,它是用於傳送binary檔案時使用。由於編碼的方式不同,會影響編碼之後的檔案大小。有些較懶惰的軟體便都一律採用Base64編碼了。


    Base64

    base64模块提供了6个函数用于Base64的编码和解码,可以将他们分为三组。

    base64.b64encode(s, altchars=None)
    base64.b64decode(s, altchars=None, validate=False)

    参数s代表需要编码/解码的数据。其中b64encode的参数s的类型必须是字节包(bytes)。b64decode的参数s可以是字节包(bytes),也可以是字符串(str)。

    由于Base64编码后的数据中可能会含有’+’或者’/’两个符号,如果编码后的数据用于url或者文件系统的路径中,就可能会导致Bug。所以base64模块提供了将编码后的数据中’+’和’/’进行替换的方法。

    参数altchars必须是长度为2的字节包,这两个符号会用于替换编码后数据中的’+’和’/’。这个参数默认是None。

    参数validate默认为False。如果它为True时,base64模块在进行解码前会先检查s中是否有非base64字母表中的字符,如果有的话则抛出错误binascii.Error: Non-base64 digit found。

    如果数据的长度不正确则会抛出错误binascii.Error: Incorrect padding。

    >>> import base64
    >>> x = base64.b64encode(b'test')
    >>> x
    b'dGVzdA=='
    >>> base64.b64decode(x)
    b'test'

    base64.standard_b64encode(s)
    base64.standard_b64decode(s)

    这组函数会直接将参数s传到上一组函数中。


    base64.urlsafe_b64encode(s)
    base64.urlsafe_b64decode(s)

    这组函数同样基于第一组函数,但进行编码后会将输出数据中的’+’和’/’替换为’-‘和’_’。解码前则将数据中的’-‘和’_’替换为’+’和’/’。

    另,Base64编码还会产生一个符号’=’,这个符号用于将数据长度填充到4的倍数。


    Base32

    base64.b32encode(s)
    base64.b32decode(s, casefold=False, map01=None)

    参数s与Base64一致。

    Base32编码后的字符范围为[2-7A-Z],是不支持小写字母的。不过当参数casefold为True时,Base32解码时可以接受小写字母的输入。但是为了安全考虑,这个参数默认为False。

    Base32的解码同时还允许将数字0替换为大写字母O,把数字1替换为大写字母I或者L。参数map01可以指定将数字1替换为哪个字符(源码中并没有限定必须是字母I或者L其中之一),当这个参数非None时,数字0总是会被替换为字母O。同样为了安全考虑,这个参数默认为None。


    Base16

    base64.b16encode(s)
    base64.b16decode(s, casefold=False)

    Base16编码后的字符范围为[0-9A-F]。

    参数s和casefold的作用与Base32一致。


    Base85

    base64.b85encode(b, pad=False)
    base64.b85decode(b)

    参数b为用于编码/解码的数据,类型要求跟Base64的参数s一致。

    参数pad为True时,在编码前会用b’\0’将数据填充到长度为4的倍数。不过在解码的时候不会移除这些填充数据。

    这组函数是在Python3.4之后新增的。


    Ascii85

    base64.a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False)

    参数b为用于编码的数据,类型必须为bytes。

    参数foldspaces为True时会用b’y’来表示4个连续的空格。

    参数wrapcol为一个整数,当wrapcol非0时,这个整数控制编码后的输出每多少个字符添加一个换行符b’\n’。

    参数pad为True时,数据在编码前会用b’\0’填充到长度为4的倍数。解码的时候不会移除这些填充数据。

    参数adobe指定了数据是否采用Adobe的格式。Adobe Ascii85的编码数据由<\~和\~>包围起来,如果这个参数为True,返回的数据会加上这对符号。


    base64.a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b’ \t\n\r\v’)

    参数b为用于编码的数据,类型可以为bytes或者str。

    参数foldspaces为True时会用b’y’来表示4个连续的空格。

    参数adobe指定了数据是否采用Adobe的格式。Adobe Ascii85的编码数据由<\~和\~>包围起来,如果这个参数为True,解码前base64会先去掉这对符号。

    参数ignorechars指定了解码时需要忽略掉的字符。默认包含了ASCII中所有的空白符。

    这组函数是在Python3.4之后新增的。

    base64模块的官方文档中提到:Base85和Ascii85使用5个字符编码4个字节,而Base64使用6个字符编码4个字节(实际上是4个字符编码3个字节),当空间不充裕时前两者会比Base64更高效。


    旧API

    base64仍然保留了一部分旧的API,用于一些特殊用途。

    base64.encode(input, output)
    base64.decode(input, output)

    这组函数使用二进制文件作为数据源,并将编码/解码后的数据写入二进制文件。


    base64.encodebytes(s)
    base64.decodebytes(s)

    encodebytes和b64encode在内部都是调用的binascii模块的b2a_base64,只不过encodebytes调用b2a_base64时newline参数使用默认值True。也就是说,encodebytes在输出数据的时候,每76个字节会添加一个换行符b’\n’。

    decodebytes和默认参数下的b64decode基本一致。只有参数类型的检查不一样,decodebytes只支持bytes类型的数据。


    base64.encodestring(s)
    base64.decodestring(s)

    这组函数在Python3.1之后就废弃了,目前会直接调用上一组函数。


    总结

    base64模块提供了对二进制数据进行编码的接口,其中包括了标准的Base64,Base32,Base16和事实标准Ascii85和Base85。通过学习这个模块,顺便学习了一下二进制数据编码的各种细节,感受颇深。有时候我们自以为了解计算机,了解互联网,其实每个人看到的都只是沧海一粟,不值一提。这个领域对于我来说还有很多未知,是等待探索的,而我也不会停止探索的脚步。


    参考资料:

    https://docs.python.org/3/library/base64.html
    http://bbs.chinaunix.net/thread-1150250-1-1.html

    感谢以上内容提供者!

    展开全文
  • 因为一个字节中存在8个 bit可以表示256个字符,而 ASCII 码只能表示0-127种字符,为了能完整地表示一个字节,可以将二进制数据转换为十六进制数据的方式来实现。所以 Hex 编码也被称作为 Base16 编码,相比于原先8位...
  • 文本数据与二进制数据

    千次阅读 2017-04-25 15:36:23
    文本数据和二进制数据的区别的简单理解: ...1、字符数据与非字符数据 字符数据有自己的编码方式,而非字符数据没有统一的...文本数据的编码方式比较固定,UNICODE或者ASCII编码,二进制数据编码方式比特为由类型决定
  • 赵泽添摘 要 数据是计算机处理的对象,从不同的处理角度来看,数据有不同的表现形态。从外部形式来看计算机颗处理数值、图、文字、声音、视频以及各种模拟信息量。从高级语言程员的角度来看,有数组、指针、结构、...
  • 重构允许您以声明方式二进制数据进行编码和解码。 它支持多种类型,使您无需编写任何解析代码即可表达多种二进制格式。 一些受支持的特性是类 C 结构、版本化结构、指针、任何类型的数组、大量编码的字符串、...
  • QDataStream 二进制数据读写

    万次阅读 2017-03-20 14:24:43
    Qt中的QDataStream类为我们的程序提供了读写二进制数据的能力。一个数据流如果是二进制编码的数据流,那么它肯定是与计算机的操作系统、...同样,我们也可以使用QDataStream去读写原生的未编码二进制数据。 QDataSt
  • 计算机二进制编码

    2020-10-17 17:00:56
    二进制编码知识。
  • ASCII码划分为两个集合:128个字符的标准ASCII码(7位二进制编码)和附加的128个字符的扩展ASCII码(8位二进制编码)。ASCII是英文American Standard Code for Information Interchange的缩写。ASCII码是目前计算机最...
  • 二进制数据: 二进制数据就是完全数字型二进制文件:"二进制文件"本来就是一种似是而非的叫法。我们知道,存在有种类繁多的不同类型(格式)的文件,如文本文件,视频文件,图像文件,数据库文件...., 文件格式的具体定义...
  • Python二进制数据处理

    万次阅读 2019-01-10 15:11:56
    目录前言str与bytesbase64模块struct模块...当然本篇文章不是去说编码的,而是主要讲讲二进制处理,python 3为我们提供了bytes,利用bytes可以一定程度上缓解编码出错的问题,因为bytes是字节序列,无所谓编码。下...
  • 网上传送二进制数据的几种方法

    千次阅读 2019-10-21 14:11:15
    一直以来,有些无线(特别是无线Modem)或者有线网络设备无法正确发送和接收某些不可打印(不可见)二进制字符, 所以需要对二进制数据做一定的编码变换, 使之成为纯文本可见字符,常见的有两种方法,一种是采用16进制...
  • mongodb存储二进制数据

    千次阅读 2018-04-13 17:46:44
    mongodb存储二进制数据mongodb 3.x存储二进制数据并不是以base64的方式,虽然在mongo客户端的查询结果以base64方式显示,请放心使用。下面来分析存储文件的存储内容。base64编码数据会增长1/3成为顾虑。 首先看...
  • 二进制编码(原码、反码、补码) 1.二进制怎么编码? 1字节 = 8位,所以它能表示的最大数当然是8位都是1(既然2进制的数只能是0或1,如果是我们常见的10进制,那就8位都为9)1字节的二进制数中,最大的数:...
  • 前端操作文件和二进制数据

    千次阅读 2020-05-12 15:35:28
    同时在浏览器中处理二进制数据的需求在不断增加,有时需要字节数组、8位、16位、32位整型数组,有必要对JS中处理二进制的知识做一次梳理。 应用场景 操作用户选中的本地二进制文件,需要用二进制流的方式上传到...
  • 二进制编码知识

    2016-05-17 20:18:12
    对于整型数据二进制的表示为符号位+数值位,对于浮点型数据,十进制转二进制方式如下将十进制浮点型数据转换为二进制时分别将整数部分和小数部分转化为二进制对于整数部分,每次除2取余直到商为0,第一个除法...
  • fiddler post二进制数据

    千次阅读 2018-08-04 12:56:05
    使用fiddler抓包时,会...2、本身数据就是二进制数据(所以本篇标题并不是特别准确)。正常抓包到的数据如下图: 一、编码转换 常见的乱码数据包如下图,注意下面还有一个“ FIDDLER: RawDisplay truncated at 1...
  • 在很多应用中,经常会直接把图片的二进制数据进行交换,比如说利用 socket 通信传送图片二进制数据,或者直接用内存数据库(例如 Redis)来传递图片二进制数据。 这个时候,当你的应用程序读到内存里的二进制图片...
  • 所以,我们见到的计算机的一切都是通过二进制才能呈现出来的。这次就会着重介绍一下进制的计算和编码。 博文目录 数的进制 进制的转换 逻辑运算 常用编码 数的表示 符号数的机器码表示 before we star.....
  • 【基础】二进制编码

    千次阅读 2017-01-07 22:25:57
    意义xxxx几种编码及转换几种编码都需要懂,而且分情况。使用这些编码的基本前提,是确定单位,比如这里探讨的是一个字节,还是int(例如java中的32位int)。以下内容我们均假设为一个字节(8位)原码及使用几点特性...
  • 从文件中按照GBK编码方式读取二进制流,将二进制数据封装到XML数据包中,经过 网络传输到另一端后,再将二进制数据按照GBK编码写回到某一文件中,二进制数据 能够获取到,但是经过字符串处理后,到另一端如何还原...
  • 二进制数据嵌入json的几种方法

    千次阅读 2016-08-30 07:06:51
    1. 在 xml 中嵌入二进制数据的几种方法通过外部实体和标记法的方式表示二进制数据;使用 MINE 数据类型来表示二进制数据(并把数据用 Base64 编码后放入CDATA节中);将二进制数据嵌入 CDATA 节中,编码格式由用户...
  • (multipart/form-data -&gt; 以二进制数据流的方式传输)
  • Json二进制数据

    千次阅读 2016-02-02 16:53:55
    如果想要传输图片等二进制文件的话,是没办法直接传输。 本文提供一种思路给大家参考,让大家可以在json传输二进制文件,如果大家有这个需求又不知怎么实现的话,也许本文能够帮到你。思想适用于所有语言,本文以...
  • 概述 通过前三篇博客,我们能够了解在通过WebSocket...本文作为WebSocket系列的第四篇内容,将会用一个简单的IM聊天应用把整个WebSocket传输二进制数据类型的内容连接起来,让用户对整个WebSocket传输二进制数据的方...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 261,417
精华内容 104,566
关键字:

二进制数据编码方式