十六進制字符串轉float數組


注意:本博客無任何硬廣、軟廣,僅為分享學習知識之意。所有外鏈中的廣告宣傳均與本博客無關,請各位看官仔細分辨。若存在侵權文章引用,請及時聯系博主“AiyaFocus”,謝謝。

注意:本博客為博主“AiyaFocus”原創,轉載請注明出處:https://www.cnblogs.com/AiyaFocus/p/14334164.html,請尊重知識,尊重原創,尊重每個人的勞動成果,謝謝。

 

一、前景提要

有個需求是MySQL數據庫中存儲了一段十六進制的值,字段類型是longblob,需要將這個值轉換並存儲到float類型的數組中。假如,該十六進制值共有248個字符(1個"F"就表示一個字符)。

二、注意要點

1. MySQL數據庫表字段類型longblob對應的Java類型是byte[]數組;

2. 1個byte=8bit,一個十六進制值,例如"F",只用4bit就可以存儲表示,所以,應該是兩個十六進制值,如"FF",對應一個byte(字節)。所以,上面說的248個十六進制字符應該對應的是124個字節。注意,這里有個大坑,導致這個問題卡了很久,數據怎么處理都不對,后面會有解釋。

3. 解析出來的單個數據為float類型的值,1個float值占4個字節。此處數據解析會有大端模式小端模式的問題,具體怎么解析,要看數據具體是怎么存的。我這邊數據是按小端模式存儲的。

  這里簡單解釋一下大端模式和小端模式,此處提供一個百度百科鏈接:

    a. 小端模式:低地址中存放的是單個數據的低字節,高地址中存放的是單個數據的高字節;例如:假設數據庫數據為“0123”,小端模式解析是按照“3210”排列解析。

    b. 大端模式:低地址中存放的是單個數據的高字節,高地址中存放的是單個數據的低字節;例如:假設數據庫數據為“0123”,大端模式解析是按照“0123”排列解析。

  什么?沒懂!原諒博主水平有限。此處放一個百度百科解釋鏈接:大小端模式 ,請自行食用。若還需要深入了解,請右轉自行百度

4. 前4個字節是不需要的,所以不用處理。(此處為當前需求中的解析格式要求,所以下面代碼也是按照這個格式解析,此處各位看官還是需要根據自己需求來)。

三、坑是什么?

上面說到有個坑,卡了很久。上面說了,MySQL數據庫表字段類型longblob對應的Java類型是byte[]數組,所以類中是用byte[]數組接收結果。本以為此處byte[]數組接收的數據應該是,比如說數據庫的數據是十六進制的"FE03"值,那么byte[]數組中,數據存儲應該為["00001111", "00001110", "00000000", "00000011"]。然后按照兩個十六進制數為一個字節進行計算,比如十六進制的"FE03"值分為"FE"和"03",對應的byte[]數組應該為["11111110", "00000011"]。然而,事實並非如此,因為一開始就錯了。怪我自己先入為主的想成數據存儲應該像我剛剛講的那樣,然而並不是。實際上類中byte[]數組存儲的並不是十六進制(如"F")對應的二進制的值(如"00001111"),而是每個字符對應的二進制的值,如"F"對應ASCII碼表中的十進制的值為70,轉換成二進制值為"0100 0110",所以類中byte[]數組存儲的是數據庫該字段值的字符串中,每個字符對應的二進制值(ASCII碼表可查。這並不是我們所希望得到的結果。這么大個坑卡了很久才發現,血淚史!(T_T)。

四、新的問題出現

既然,我們已經發現了問題的關鍵。那么接下來就是轉換的數據的問題了,如何將"F"這個字符對應的byte值(也就是"01000110")轉換成十六進制對應的二進制的值(也就是我們真正需要的值"00001111"),成了一個新的問題。目前我能想到的就是先將類中byte[]數組中的每一個元素進行轉換,轉換成對應的字符,然后用“switch……case……”選擇結構依次進行比對。如byte[]數組中一個元素值為"01000110",轉換成字符則為"F",用“switch……case……”選擇結構找到對應的“case”之后,將byte[]數組中該元素值,直接修改為"00001111",其他的以此類推。然后再將兩個十六進制對應的二進制值,進行位運算,得到我們真正想要的數據,如十六進制的"F"和"E",分別對應byte二進制值為"00001111"和"00001110"。由於"F"在前,"E"在后,所以兩兩組合,"FE"對應的byte二進制值應為"11111110",這才是我們真正需要的byte[]數組。然后才是將這個byte[]數組利用位運算處理轉換成我們需要的float[]數組。

PS:關於什么是位運算,以及二進制的原碼、反碼、補碼運算。以下給出參考文章鏈接及B站視頻教程鏈接,或者自行百度。(注意:如果只想解決問題,不想過多過深地去了解這方面的知識,聽博主在這啰里吧嗦的,請直接移步下面示例代碼區域。:P)

位運算參考資料鏈接:位運算(&、|、^、~、>>、<<)位運算有什么奇淫技巧?

二進制的原碼、反碼、補碼運算:原碼,反碼,補碼 詳解二進制(原碼、反碼、補碼)二進制運算(原碼、反碼、補碼)

B站學習參考視頻:進制轉換和位運算專題 

 

注意:本博客無任何硬廣、軟廣,僅為分享學習知識之意。所有外鏈中的廣告宣傳均與本博客無關,請各位看官仔細分辨。若存在侵權文章引用,請及時聯系博主“AiyaFocus”,謝謝。

注意:本博客為博主“AiyaFocus”原創,轉載請注明出處:https://www.cnblogs.com/AiyaFocus/p/14334164.html,請尊重知識,尊重原創,尊重每個人的勞動成果,謝謝。

 

五、新的解決辦法

不過我並沒有使用這種方法,因為在這之前我已經發現了更好的辦法(:D)。那就是使用Apache提供的用於摘要運算、編碼解碼的工具包“commons-codec”,使用該工具包中Hex類的decodeHex靜態方法(具體使用參考下面代碼)。並且使用這個方法有個驚喜。就是,本來按照我的思路,需要先將字符串中字符對應的byte值轉換成十六進制對應的二進制的值,整個byte[]數組都像這樣轉換之后,再用位運算符,進行兩兩組合,最終得到我們真正需要的byte[]數組。但這個方法已經幫我們把這兩步都搞定了,只用調用decodeHex這個靜態方法並傳入十六進制的字符串,返回的結果就是我們真正需要的byte[]數組,然后再將此byte[]數組轉換成float[]數組即可。該工具包后面我會以附件的形式上傳,需要的小伙伴可以自行下載。點擊此處下載:commons-codec-1.12.zipPS:因博客園上傳文件類型限制,無法上傳.jar文件,所以壓縮成zip了,下載后解壓出來即可使用該jar文件,若有幸被創建博客園的大佬及官方團隊看到,建議允許上傳的文件類型加入.jar文件類型哈(:P)。

六、示例代碼

假設一個blob文件中存儲以下一段十六進制值的字符串(共248個十六進制的字符),現需要按照上面的要求解析該字符串,得到float數組。

1 fec9d4493b559c3f957c3b3fae17fa3e1b63d83c28231d3d53ebc33b50e9d23b5c6f983ceb3e283dcbf0d63cd0c1e63ca73adc3afb5d1b3bd74c9a3b4b61553c3f9c7a3d1e7d7a3ced44e13bcdc1043b2d872e3a2cc2803ad951d93bf23c683beefaf63bb1b7703b0a285b3bfdd47f3c95e9ea3b0b60283d0215613b

 

方法一:

 1     public static void main(String[] args) throws Exception {
 2 
 3         // 1.將文件內容讀到字節數組中
 4         // a. 通過帶緩沖區的文件輸入流,加載文件
 5         BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
 6                 "./blob"));
 7         // b. 通過available()獲得文件內容的字符個數(文件內容長度)
 8         int len = bis.available();
 9         // c. 根據獲得的字符個數,創建相應長度的字節數組,用於存儲文件內容
10         byte[] b = new byte[len];
11         // d. 將這個輸入流中的內容讀取存儲到b字節數組中
12         bis.read(b);
13         // e. 判斷流是否非空,若不為空則關閉流
14         if (bis != null) {
15             bis.close();
16         }
17 
18         // 2.將傳入的存儲十六進制字符串的byte數組,轉換成真正存儲每兩個十六進制字符對應的一個二進制值的數組
19         // a. 先將該數組轉換成十六進制的字符串
20         String hexString = new String(b);
21         /*
22          * b. 利用Apache提供的用於摘要運算、編碼解碼的工具包commons-codec,
23          * 將該十六進制的字符串,轉換成存儲每兩個十六進制字符對應的一個二進制值的數組
24          */
25         byte[] data = Hex.decodeHex(hexString);
26         // c. 定義一個float數組變量,指定要返回的float數組的長度
27         // 根據解析規則,前4個字節是不需要的,所以用處理好的byte數組長度減4
28         float[] f = new float[(data.length - 4) / 4];
29         // d. 按照小端模式處理數據,並轉換成float數值存入float數組中
30         // 根據解析規則,前4個字節是不需要的,所以下標從4開始
31         for (int i = 4, j = 0; i < data.length; i += 4, j++) {
32             int temp = (data[i] & 0xff);
33             temp = temp | (data[i + 1] & 0xff) << 8;
34             temp = temp | (data[i + 2] & 0xff) << 16;
35             temp = temp | (data[i + 3] & 0xff) << 24;
36             // 將處理后的float值存入float數組中
37             f[j] = Float.intBitsToFloat(temp);
38         }
39 
40         // 3.輸出打印該float[]數組的長度及其中的值
41         System.out.println("轉換后float[]數組的長度為:" + f.length);
42         System.out.println("轉換后float[]數組中的值為:" + Arrays.toString(f));
43     }

 

方法二:

 1     public static void main(String[] args) throws Exception {
 2 
 3         // 1.將文件內容讀到字節數組中
 4         // a. 通過帶緩沖區的文件輸入流,加載文件
 5         BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
 6                 "./blob"));
 7         // b. 根據available()方法獲得文件內容的字符個數(文件內容長度),創建相應長度的字節數組,用於存儲文件內容
 8         byte[] b = new byte[bis.available()];
 9         // c. 將這個輸入流中的內容讀取存儲到b字節數組中
10         bis.read(b);
11         // d. 判斷流是否非空,若不為空則關閉流
12         if (bis != null) {
13             bis.close();
14         }
15 
16         // 2.將傳入的存儲十六進制字符串的byte數組,轉換成真正存儲每兩個十六進制字符對應的一個二進制值的數組
17         // a. 先將該數組轉換成十六進制的字符串
18         String hexString = new String(b);
19         /*
20          * b. 利用Apache提供的用於摘要運算、編碼解碼的工具包commons-codec,
21          * 將該十六進制的字符串,轉換成存儲每兩個十六進制字符對應的一個二進制值的數組
22          */
23         byte[] data = Hex.decodeHex(hexString);
24         // c. 定義一個float數組變量,指定要返回的float數組的長度
25         // 根據解析規則,前4個字節是不需要的,所以用處理好的byte數組長度減4
26         float[] f = new float[(data.length - 4) / 4];
27         /*
28          * d. 創建字節緩沖區對象。
29          * 利用wrap()方法將指定的byte[]數組包裝到緩沖區中,
30          * 並利用order()方法設定此緩沖區的字節順序,
31          * 要么是BIG_ENDIAN(大端),要么是LITTLE_ENDIAN(小端),
32          * 字節緩沖區的初始順序始終是BIG_ENDIAN(大端)。
33          */
34         ByteBuffer byteBuffer = ByteBuffer.wrap(data).order(
35                 ByteOrder.LITTLE_ENDIAN);
36         // e. 根據解析規則,前4個字節是不需要的,所以讀前4個字節到discardByte數組中,丟棄這前4個字節
37         // 創建discardByte數組,存儲需要丟棄的字節值
38         byte[] discardByte = new byte[4];
39         // 獲取字節緩沖區中要丟棄的字節,從下標為0開始,讀取discardByte該數組長度的字節,並存入discardByte該數組中
40         // 此時字節緩沖區中當前位置已經發生改變
41         byteBuffer.get(discardByte, 0, discardByte.length);
42         // f. 循環float[]數組,從byteBuffer字節緩沖區中繼續讀取數據,並存入float[]數組中
43         for (int i = 0; i < f.length; i++) {
44             // 調用getFloat()方法,讀取字節緩沖區中的float值,並將其存入float[]數組中
45             // getFloat()方法:讀取此緩沖區的當前位置之后的4個字節,根據當前的字節順序將它們組成float值,然后將該位置增加4。 
46             f[i] = byteBuffer.getFloat();
47         }
48 
49         // 3.輸出打印該float[]數組的長度及其中的值
50         System.out.println("轉換后float[]數組的長度為:" + f.length);
51         System.out.println("轉換后float[]數組中的值為:" + Arrays.toString(f));
52     }

 

輸出結果:

1 轉換后float[]數組的長度為:30
2 轉換后float[]數組中的值為:[1.221351, 0.7323697, 0.4884619, 0.026414445, 0.038363606, 0.0059789806, 0.0064365044, 0.018607788, 0.04107563, 0.026237866, 0.028168589, 0.0016802148, 0.002370714, 0.004708867, 0.013023685, 0.061184164, 0.015288619, 0.0068746717, 0.0020257116, 6.657716E-4, 9.823493E-4, 0.0066320715, 0.0035436717, 0.0075372374, 0.0036730582, 0.0033440613, 0.015614745, 0.0071689584, 0.04110722, 0.0034344797]

 

注意:

1. 示例代碼中異常並沒有針對性的處理,僅為展示整個程序思路及開發代碼。正常開發中,需對程序中存在的不同的異常捕獲進行不同的處理;

2. 方法一和方法二兩段代碼僅第2步,解析方式不一樣,方法二利用了Java默認提供的ByteBuffer類進行處理,方法一則更接近於底層邏輯實現;

3. 輸出結果為MyEclipse中測試的結果,Idea中可能不太一樣,結果可能帶e的多少次方,會更精確一些,但大體結果一致;

 

注意:本博客無任何硬廣、軟廣,僅為分享學習知識之意。所有外鏈中的廣告宣傳均與本博客無關,請各位看官仔細分辨。若存在侵權文章引用,請及時聯系博主“AiyaFocus”,謝謝。

注意:本博客為博主“AiyaFocus”原創,轉載請注明出處:https://www.cnblogs.com/AiyaFocus/p/14334164.html,請尊重知識,尊重原創,尊重每個人的勞動成果,謝謝。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM