前語:不要為了讀文章而讀文章,一定要帶著問題來讀文章,勤思考。
本文鏈接:?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),字節通??s寫為B,比特通常縮寫為b。字節的大小是8Bit,即字節的范圍是0000 0000-?1111 1111,對于
無符號型,它表示的十進制范圍是[0,255],對于
有符號型,高一位表示符號位,它表示的十進制范圍是[-128,127]。
計算機如何儲存數據
計算機只認識0和1(因為計算機只有高低電平兩個狀態),數據要想通過計算機儲存或者傳輸,首先是要把數據轉成計算機能認識的格式即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 0000,255表示為1111 1111,剛好滿足了要求,可以表示256個數據。再看有符號的,若是用
原碼表示,0表示為0000 000。因為咱們有符號,所以應該也有個
負0(雖然它還是0)1000 0000。這樣的話那就有2個0,也就是只能表示255個數據,不能夠滿足我們的要求。而用補碼則很好的解決了這個問題。
字符
在計算機中,對
非數值的字符進行處理時,要對字符進行數字化,即用二進制編碼來表示字符。其中西文字符最常用到的編碼方案有ASCII編碼和EBCDIC編碼。對于漢字,我國也制定的相應的編碼方案,比如 GBK,GB2312等。比如字符a的ASCII碼十進制值為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對應的二進制分別是11010110和11010000(補碼),即漢字中對應的二進制為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代表正數 }} |
調試看看,字符串EF有E和F兩個字符,它們對應的十進制ASCII碼分別是69和70我們發現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字符串转成数字_计算机储存数字和字符的方法你了解多少?的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。