注意:本博客⽆任何硬⼴、软⼴,仅为分享学习知识之意。所有外链中的⼴告宣传均与本博客⽆关,请各位看官仔细分辨。若存在侵权⽂章引⽤,请及时联系博主“AiyaFocus”,
谢谢。
注意:本博客为博主“AiyaFocus”原创,转载请注明出处:,请尊重知识,尊重原创,尊重每个⼈的劳动成果,谢谢。
⼀、前景提要
李健的歌有个需求是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[]数组存储的是数据库该字段值的字符串中,每个字符对应的⼆进制值()。这并
不是我们所希望得到的结果。这么⼤个坑卡了很久才发现,⾎泪史!(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”原创,转载请注明出处:,请尊重知识,尊重原创,尊重每个⼈的劳动成果,谢谢。
五、新的解决办法
不过我并没有使⽤这种⽅法,因为在这之前我已经发现了更好的办法(:D)。那就是使⽤Apache提供的⽤于摘要运算、编码解码的⼯具包“commons-codec”,使⽤该⼯具包中
Hex类的decodeHex静态⽅法(具体使⽤参考下⾯代码)。并且使⽤这个⽅法有个惊喜。就是,本来按照我的思路,需要先将字符串中字符对应的byte值转换成⼗六进制对应的
⼆进制的值,整个byte[]数组都像这样转换之后,再⽤位运算符,进⾏两两组合,最终得到我们真正需要的byte[]数组。但这个⽅法已经帮我们把这两步都搞定了,只⽤调⽤
decodeHex这个静态⽅法并传⼊⼗六进制的字符串,返回的结果就是我们真正需要的byte[]数组,然后再将此byte[]数组转换成float[]数组即可。该⼯具包后⾯我会以附件的形式上
传,需要的⼩伙伴可以⾃⾏下载。点击此处下载:。PS:因博客园上传⽂件类型限制,⽆法上传.jar⽂件,所以压缩成zip了,下载后解压出来即可使⽤该jar⽂件,若有幸被创建
博客园的⼤佬及官⽅团队看到,建议允许上传的⽂件类型加⼊.jar⽂件类型哈(:P)。
六、⽰例代码
假设⼀个blob⽂件中存储以下⼀段⼗六进制值的字符串(共248个⼗六进制的字符),现需要按照上⾯的要求解析该字符串,得到float数组。
1 fec9d4493b559c3f957c3b3fae17fa3e1b63d83c28231d3d53ebc33b50e9d23b5c6f983ceb3e283dcbf0d63cd0c1e63ca73adc3afb5d1b3bd74c9a3b4b61553c3f9c7a3d1e7d7a3ced44e13bcdc1043b2d872e3a2cc2803ad951d93bf23c683beefaf63bb1b770
⽅法⼀:
1public static void main(String[] args) throws Exception {
2
3// 1.将⽂件内容读到字节数组中
4// a. 通过带缓冲区的⽂件输⼊流,加载⽂件
5 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
6 "./blob"));
7// b. 通过available()获得⽂件内容的字符个数(⽂件内容长度)
网曝青簪行换男主8int len = bis.available();
9// c. 根据获得的字符个数,创建相应长度的字节数组,⽤于存储⽂件内容
10byte[] b = new byte[len];
11// d. 将这个输⼊流中的内容读取存储到b字节数组中
12 ad(b);
13// e. 判断流是否⾮空,若不为空则关闭流
14if (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*/
25byte[] data = Hex.decodeHex(hexString);
26// c. 定义⼀个float数组变量,指定要返回的float数组的长度
27// 根据解析规则,前4个字节是不需要的,所以⽤处理好的byte数组长度减4
28float[] f = new float[(data.length - 4) / 4];
29// d. 按照⼩端模式处理数据,并转换成float数值存⼊float数组中
30// 根据解析规则,前4个字节是不需要的,所以下标从4开始
31for (int i = 4, j = 0; i < data.length; i += 4, j++) {
32int 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[]数组中的值为:" + String(f));
43 }
⽅法⼆:
1public static void main(String[] args) throws Exception {
2
3// 1.将⽂件内容读到字节数组中
4// a. 通过带缓冲区的⽂件输⼊流,加载⽂件
5 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
6 "./blob"));
7// b. 根据available()⽅法获得⽂件内容的字符个数(⽂件内容长度),创建相应长度的字节数组,⽤于存储⽂件内容
8byte[] b = new byte[bis.available()];
9// c. 将这个输⼊流中的内容读取存储到b字节数组中
10 ad(b);
11// d. 判断流是否⾮空,若不为空则关闭流
12if (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*/
23byte[] data = Hex.decodeHex(hexString);
24// c. 定义⼀个float数组变量,指定要返回的float数组的长度
25// 根据解析规则,前4个字节是不需要的,所以⽤处理好的byte数组长度减4
26float[] 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数组,存储需要丢弃的字节值
38byte[] discardByte = new byte[4];
39// 获取字节缓冲区中要丢弃的字节,从下标为0开始,读取discardByte该数组长度的字节,并存⼊discardByte该数组中
40// 此时字节缓冲区中当前位置已经发⽣改变
41 (discardByte, 0, discardByte.length);
42// f. 循环float[]数组,从byteBuffer字节缓冲区中继续读取数据,并存⼊float[]数组中
43for (int i = 0; i < f.length; i++) {
44// 调⽤getFloat()⽅法,读取字节缓冲区中的float值,并将其存⼊float[]数组中
45// getFloat()⽅法:读取此缓冲区的当前位置之后的4个字节,根据当前的字节顺序将它们组成float值,然后将该位置增加4。
46 f[i] = Float();
47 }
48
49// 3.输出打印该float[]数组的长度及其中的值
50 System.out.println("转换后float[]数组的长度为:" + f.length);
51 System.out.println("转换后float[]数组中的值为:" + String(f));
52 }
输出结果:郭美美baby
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.06118
注意:
1. ⽰例代码中异常并没有针对性的处理,仅为展⽰整个程序思路及开发代码。正常开发中,需对程序中存在的不同的异常捕获进⾏不同的处理;
2. ⽅法⼀和⽅法⼆两段代码仅第2步,解析⽅式不⼀样,⽅法⼆利⽤了Java默认提供的ByteBuffer类进⾏处理,⽅法⼀则更接近于底层逻辑实现;
3. 输出结果为MyEclipse中测试的结果,Idea中可能不太⼀样,结果可能带e的多少次⽅,会更精确⼀些,但⼤体结果⼀致;
注意:本博客⽆任何硬⼴、软⼴,仅为分享学习知识之意。所有外链中的⼴告宣传均与本博客⽆关,请各位看官仔细分辨。若存在侵权⽂章引⽤,请及时联系博主“AiyaFocus”,
谢谢。
注意:本博客为博主“AiyaFocus”原创,转载请注明出处:,请尊重知识,尊重原创,尊重每个⼈的劳动成果,谢谢。
发布评论