前语:不要为了读文章而读文章,一定要带着问题来读文章,勤思考。

本文链接: http://1t.click/J7E

前言

最近在学习中涉及到计算机储存、传输数字和字符等操作,由于对字节、2进制、10进制、16进制、ASCII码的概念以及它们之间的关系和转换理解的不够透彻,导致在通讯、MD5消息摘要算法等时候出现问题,是因为数据转成计算机认识的01的这个环节出现问题。由于这个问题并不是那么容易发现,所以我也算是花了挺多时间才解决了这个问题,记录下解决过程,顺便也当复习一下计算机组成原理。

ASCII码

在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码,统一规定了上述常用符号用哪些二进制数来表示。ASCII 码一共规定了128个字符(0000 0000-0111 1111)的编码,比如空格SPACE是32(二进制0010 0000),大写的字母A是65(二进制0100 0001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位(低7位),最前面的一位(高1位)统一规定为0(不要和数字的符号位搞混)。当然除了ASCII码,还有UTF-8、GBK等。字节字节(Byte)普通计算机系统能读取和定位到最小信息单位,即我们通过计算机储存和传输数据的时候都是先把数据转成字节。字节即Byte,一个字节代表8个比特(Bit),字节通常缩写为B,比特通常缩写为b。字节的大小是8Bit,即字节的范围是0000 00001111 1111,对于无符号型,它表示的十进制范围是[0,255],对于有符号型,高一位表示符号位,它表示的十进制范围是[-128,127]。

计算机如何储存数据

计算机只认识01(因为计算机只有高低电平两个状态),数据要想通过计算机储存或者传输,首先是要把数据转成计算机能认识的格式即01数据。我们举个例子,以储存十进制数字28-28为例,首先将十进制数转成二进制。

需要注意的是: 数字在计算机中储存的是补码,而字符是在计算机中储存的是字符对应的编码(不要和数字的补码搞混)。

数字

储存十进制数字28-28为例,首先将十进制数转成二进制,高1位为0代表正数,为1代表负数

12
28(10) = 0001 1100(2)(原码)-28(10) = 1001 1100(2)(原码)

然后计算机将二进制数字进行补码运算,运算结果如下

12
28(10) = 0001 1100(2)(原码) =  0001 1100(2)(补码)-28(10) = 1001 1100(2)(原码) = 1110 0100(2)(补码)

然后计算机保存的就是补码,当要取出数据的时候,就将补码逆运算一下,即可求出原码,再将原码转换一下就可以得到真实的数据了。

下面以Java语言演示这个过程,首先我们要清楚Java的byte、short、int、long都是有符号的(signed)

123456789101112131415161718
public class test {    public static void main(String[] args) throws NoSuchAlgorithmException {        int a1 = 28;        int a2 = -28;//        转成二进制表示        String b1 = Integer.toBinaryString(a1);        String b2 = Integer.toBinaryString(a2);//        转成无无符号表示        String b3 = Integer.toUnsignedString(a1);        String b4 = Integer.toUnsignedString(a2);        System.out.println("28储存到计算机后为:" + b1);        System.out.println("-28储存到计算机后为:" + b2);        System.out.println("取出储存的28 以无符号表示:" + b3);        System.out.println("取出储存的-28 以无符号表示:" + b4);    }}

运行输出:

1234
28储存到计算机后为:11100-28储存到计算机后为:11111111111111111111111111100100取出储存的28 以无符号表示:28取出储存的-28 以无符号表示:4294967268

我们验证一下结果,验证了计算机确实是以补码的方式储存数字。这里有个小问题,就是我们知道int型有4个字节即32个比特,但是28却输出了111005个比特而已,是因为toBinaryString()方法把11100前面的0给忽略了。取出的时候,我们以无符号的标准去处理,导致取出存入的-28结果是4294967268和我们存入的不一样,这是因为-28是负数,负数的补码和原码不一样,而用无符号处理的话就是直接将11111111111111111111111111100100转成结果了。而为什么28用有无符号处理结果都一样是因为正数的原码和补码一样,这样验证了Java的数据类型都是有符号的。至于计算机为什么用补码来储存数字,而不是原码,原因是:拿单字节整数来说,无符号型,其表示范围是[0,255],总共表示了256个数据。有符号型,其表示范围是[-128,127]。先看无符号,原码和补码都一样,0表示为0000 0000255表示为1111 1111,刚好满足了要求,可以表示256个数据。再看有符号的,若是用原码表示,0表示为0000 000。因为咱们有符号,所以应该也有个负0(虽然它还是0)1000 0000。这样的话那就有2个0,也就是只能表示255个数据,不能够满足我们的要求。而用补码则很好的解决了这个问题。

字符

在计算机中,对非数值的字符进行处理时,要对字符进行数字化,即用二进制编码来表示字符。其中西文字符最常用到的编码方案有ASCII编码和EBCDIC编码。对于汉字,我国也制定的相应的编码方案,比如 GBK,GB2312等。比如字符aASCII码十进制值为97,在计算机中用二进制表示就是 01100001。下面同样用Java来演示计算机是如何储存字符的。1.采用UTF-8和GBK两种编码储存汉字

12345678910111213141516171819202122
public class test {    public static void main(String[] args) throws NoSuchAlgorithmException {        String a1 = "中";//        采用两种不同的编码储存"中"这个汉字 比较两种编码        byte[] b1 = a1.getBytes("GBK");        byte[] b2 = a1.getBytes("UTF-8");        String c1 = binary(b1,2);        String c2= binary(b2,2);        System.out.println("GBK储存对应的二进制:" + c1);        System.out.println("UTF-8储存对应的二进制:" +c2);    }    /**     * 将byte[]转为各种进制的字符串     * @param bytes byte[]     * @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制     * @return 转换后的字符串     */    public static String binary(byte[] bytes, int radix){        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数    }}

我们调试看看,发现GBK编码采用2个字节储存,储存的数据分别是10进制的-42-48对应的二进制分别是1101011011010000(补码),即汉字对应的二进制为1101011011010000,即16进制的D6D0,查看GBK对照表,发现16进制编码D6D0对应的汉字确实是而UTF-8编码采用3个字节储存,同理将对应的二进制111001001011100010101101转成16进制,为E4B8AD,通过UTF-8编码查询,发现汉字对应的16进制编码确实是E4B8AD2.储存字符

12345678910111213141516171819
public class test {    public static void main(String[] args) throws NoSuchAlgorithmException {        String a1 = "EF";//        将字符串转成字节数组        byte[] b1 = a1.getBytes();        String c1 = binary(b1,2);        System.out.println("对应的二进制:" + c1);    }    /**     * 将byte[]转为各种进制的字符串     * @param bytes byte[]     * @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制     * @return 转换后的字符串     */    public static String binary(byte[] bytes, int radix){        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数    }}

调试看看,字符串EFEF两个字符,它们对应的十进制ASCII码分别是6970我们发现Java的getBytes()方法是将字符串的每一个字符都储存到一个字节的,如果我们想把EF储存在一个字节里面,即EF是一个整体的,一个字节,不能拆分,那我们可以把EF放在一个字节里面(byte)(0xEF),声明它是一个字节,不是字符,不用再将它转成字符对应的编码。下面说说我在进行MD5消息摘要算法时候遇到的坑,我要对QQ号对应的Hex进行MD5算法散列,这里我举例QQ号的10进制为12345678,对应的16进制为00BC614E(因为QQ号固定长度4个字节,所以前面补了2个0),一开始我是以下面的方式进行MD5算法的

12345678910111213141516171819202122232425
public class test {    public static void main(String[] args) throws NoSuchAlgorithmException {        String qq = "00BC614E";//        将字符串转成字节数组        byte[] b1 = qq.getBytes();        MessageDigest md = MessageDigest.getInstance("MD5");        md.update(b1);//        得到MD5后的哈希值        byte[] hash = md.digest();//        将结构转成16进制        String c1 = binary(hash,16);        System.out.println("对应的16进制:" + c1);    }    /**     * 将byte[]转为各种进制的字符串     * @param bytes byte[]     * @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制     * @return 转换后的字符串     */    public static String binary(byte[] bytes, int radix){        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数    }}

调试可以看到上面的代码其实是将字符串00BC614E转成了8个字节,然后再对这8个字节进行散列,这也是基于字符串进行的MD5散列,和通过网上一些网站散列得到的值是一样的但是这个哈希值和预想的结果不一致,后来才知道预想的结果是基于字节进行的MD5散列,也就是00BC614E应该分成4个字节(00、BC、61、4E)而不是8个字节(0、0、B、C、6、1、4、E),然后通过修改代码

123456789101112131415161718192021222324252627
public class test {    public static void main(String[] args) throws NoSuchAlgorithmException {//        String qq = "00BC614E";//        将字符串转成字节数组//        byte[] b1 = qq.getBytes();        byte[] b1 = {(byte)(0x00),(byte)(0xBC),(byte)(0x61),(byte)(0x4E)};        MessageDigest md = MessageDigest.getInstance("MD5");        md.update(b1);//        得到MD5后的哈希值        byte[] hash = md.digest();//        将结构转成16进制        String c1 = binary(hash,16);        System.out.println("对应的16进制:" + c1);    }    /**     * 将byte[]转为各种进制的字符串     * @param bytes byte[]     * @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制     * @return 转换后的字符串     */    public static String binary(byte[] bytes, int radix){        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数    }}

使用(byte)声明是一个字节,不是字符,不用再将它转成字符对应的编码。00、BC、61、4E分别是一个字节,当然因为字节为8个比特,能表示256个数字,因为Java的数据类型是有符号的,所以8个比特能表示的10进制范围是[-128,127],所以(byte)(x) x不能小于-128和不能大于127,否则会溢出,溢出的部分数据会丢失。热文推荐你知道SQL的这些错误用法吗?提高面试通过率?首先要具备以下技能。听说这10道Java面试题90%的人都不会!!!同时,分享一份Java面试资料给大家,覆盖了算法题目、常见面试题、JVM、锁、高并发、反射、Spring原理、微服务、Zookeeper、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 面试 领取。

mybatis字符串转成数字_计算机储存数字和字符的方法你了解多少?相关推荐

  1. JS字符串过滤数字_过滤大写数字

    JS字符串过滤数字_过滤大写数字 代码案例: //数字替换 if(data.summary){data.summary=data.summary.replace(/[\d|壹|贰|叁|肆|伍|陆|柒| ...

  2. mybatis项目报错:java.sql.SQLException: ORA-00911: 无效字符 解决方法

    mybatis项目报错:java.sql.SQLException: ORA-00911: 无效字符 解决方法 参考文章: (1)mybatis项目报错:java.sql.SQLException: ...

  3. oracle将字符串转成数组_【算法打卡】上升下降字符串

    小时候的偶像,现在还是 难度:简单 题目: 给你一个字符串 s ,请你根据下面的算法重新构造字符串: 从 s 中选出 最小 的字符,将它 接在 结果字符串的后面. 从 s 剩余字符中选出 最小 的字符 ...

  4. c字符串分割成数组_数组与字符串

    定义数组时,应该注意以下几点: (1) 数组使用的是方括号[ ],不要误写成小括号( ). (2) 常量表达式的值必须是一个正整数值,不能含有变量,但是可以是符号常数或常量表达式. (3) 数组定义后 ...

  5. java中数学的头文件_计算机编程 - 数字(Numbers)

    计算机编程 - 数字(Numbers) 每种编程语言都支持操作不同类型的数字,例如简单的整数和浮点数. C,Java和Python根据其性质将这些数字分为几类. 让我们回过头来查看数据类型章节,其中列 ...

  6. java如果把字符串转成对象_为什么Java中的字符串对象是不可变的,有什么好处?...

    专注于Java领域优质技术号,欢迎关注 原创: 阿杜的世界 阅读本文大概需要 4分钟. 所谓不可变对象,是指一个对象在创建后,它的内部状态不会被改变的对象.这意味着当我们将一个不可变对象的引用赋值给某 ...

  7. 计算机组成原理数字逻辑,计算机组成原理数字逻辑.ppt

    文档介绍: 计算机组成原理数字逻辑1数字量和模拟量模拟量:可以在一定范围内取任意实数值的物理量,如:温度.压力.距离和时间等.数字量:在时间上和数量上都是离散的物理量,如:自动生产线上的零件记录量,台 ...

  8. java吸血鬼数字_[求助]吸血鬼数字

    [求助]吸血鬼数字 public class xixuegui { public static void main (String[] args) { int i,j,k,a,b,c,d,n,m; f ...

  9. python如何增加字符串_在python中增加字符的方法

    在本教程中,我们将看到在Python中增加字符的不同方法. 类型转换 首先让我们看看如果在不进行类型转换的情况下向char添加int会发生什么. 示例## str initialization cha ...

最新文章

  1. YOLOv3模型剪枝,瘦身80%,提速100%,精度基本不变
  2. 2018-1-8 Linux学习笔记
  3. struts+hibernate+oracle+easyui实现lazyout组件的简单案例——OpSessionview实现
  4. 中移4G模块-ML302-OpenCpu开发-ADC
  5. 前端—每天5道面试题(十二)
  6. Hinton反思新作:我说反向传播不好,但还是没谁能颠覆它
  7. C++求解汉字字符串的最长公共子序列 动态规划
  8. Hadoop大数据技术原理与应用-第一章初识Hadoop
  9. ArcGIS Runtime SDK for Android 读取tpk、vtpk
  10. 华住2.0? 5亿万豪用户信息泄漏! 个人隐私如何保护? 离不开区块链
  11. vue 实现ps图片编辑_Vue项目图片剪切上传——vue-cropper的使用
  12. OpenCV中使用Eigenfaces 或 Fisherfaces进行人脸识别
  13. bxSlider——一个精悍的拥有一大波焦点图轮播滑动特效的Js程序
  14. 计算机硬件科普——GPU
  15. is not marked as executable. If this was downloaded or copied form an untrusted source
  16. 先进先出-FCFS-调度算法-C语言实现
  17. IPV6地址与前缀分配
  18. s7-200高速脉冲输出与高速计数器讲解
  19. 学习Linux命令(43)
  20. python降低图片分辨率_使用numpy快速降低图像分辨率的Python代码

热门文章

  1. PHP多进程处理并行处理任务实例
  2. Centos搭建SVN服务器及配置文件
  3. 协同工作php,PHPOA:灵活、高效、协同,让企业高效运转
  4. 云海技术u盘怎么恢复成普通盘_BITLOCKER加密中断数据无法读取恢复一例
  5. 分数的拆分原理和方法_常见的节税原理你知道吗?
  6. c语言int a什么意思,问一下吧里大神 int a = a; 这么定义是什么意思?
  7. stl make_heap_通过使用make_heap()创建堆| C ++ STL
  8. Java PriorityQueue poll()方法与示例
  9. java list过滤重复的数据_List 去除重复数据的 5 种正确姿势!
  10. 更换mysql_这些被你忽视的MySQL细节,可能会让你丢饭碗!