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

本文探讨了MyBatis中将字符串转化为数字的过程,并介绍了计算机内部如何存储数字和字符,包括ASCII码的概念,以及数字和字符在内存中的表示形式,特别是涉及到计算机储存数字的补码表示法。
摘要由CSDN通过智能技术生成

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

6a11eaf9ee4de295995d6547bc6506ed.png 本文链接: 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。字节的大小是 8 Bit,即字节的范围是 0000 0000  -  1111 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却输出了 11100 5个比特而已,是因为 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代表正数    }}
a4e7f45a83bdad16b72619cd1a0a7173.png 我们调试看看,发现GBK编码采用 2 个字节储存,储存的数据分别是 10 进制的 -42-48 对应的二进制分别是 1101011011010000 (补码),即汉字 对应的二进制为 1101011011010000 ,即16进制的 D6D0 ,查看GBK对照表,发现16进制编码 D6D0 对应的汉字确实是 33c38c985cfa14cc501054c72450bbd4.png 而UTF-8编码采用 3 个字节储存,同理将对应的二进制 111001001011100010101101 转成16进制,为E4B8AD,通过UTF-8编码查询,发现汉字 对应的16进制编码确实是 E4B8AD 110bd345a60312b20f5832e3e10f6cb5.png 2.储存字符
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代表正数    }}
570436363bccfc5238d208118d28ff3c.png 调试看看,字符串 EFEF 两个字符,它们对应的十进制ASCII码分别是 6970 36f02f9d40526c148769e59cecfd3c9c.png 我们发现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代表正数    }}
02b9b0de627e11f00026419307384e5b.png 调试可以看到上面的代码其实是将字符串 00BC614E 转成了 8 个字节,然后再对这8个字节进行散列,这也是基于字符串进行的 MD5 散列,和通过网上一些网站散列得到的值是一样的 c4c2296bb0cfafd2faefdf748d0e18ed.png 但是这个哈希值和预想的结果不一致,后来才知道预想的结果是基于字节进行的 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代表正数    }}
6a2cb3748ddddb80a3785fb9f4e4abc2.png 使用 (byte) 声明是一个字节,不是字符,不用再将它转成字符对应的编码。00、BC、61、4E分别是一个字节,当然因为字节为 8 个比特,能表示256个数字,因为Java的数据类型是有符号的,所以 8 个比特能表示的 10 进制范围是[-128,127],所以(byte)(x) x不能小于 -128 和不能大于 127 ,否则会溢出,溢出的部分数据会丢失。 热文推荐你知道SQL的这些错误用法吗?提高面试通过率?首先要具备以下技能。听说这10道Java面试题90%的人都不会!!! b4bd5f5d62ab77e0943dc3b79a4d4050.png 同时,分享一份Java面试资料给大家,覆盖了算法题目、常见面试题、JVM、锁、高并发、反射、Spring原理、微服务、Zookeeper、数据库、数据结构等等。 获取方式:点“在看”,关注公众号并回复 面试 领取。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值