Java byte[]数组转换成16进制字符,为什么要加0x100

t5dzxzpd
  • 7

byte数组转为字符串,Integer.toString((bytes[i] & 0xff) + 0x100, 16)中,为什么要加0x100,看不懂注释,求大佬解释
看懂了 0到f只输出了一位

public static String byte2Hex(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        // 遍历byte[]数组,将每个byte数字转换成16进制字符,再拼接起来成字符串
        for (int i = 0; i < bytes.length; i++) {
            // 每个byte转换成16进制字符时,bytes[i] & 0xff如果高位是0,输出将会去掉,所以+0x100(在更高位加1),再截取后两位字符
            builder.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        return builder.toString();
    }
public class MyClass {
    public static void main(String[] args) {
        byte[] ba=new byte[256];
        for (int i = -128; i <= 127; i++) {
            ba[i+128]=(byte)i;
            System.out.println(ba[i+128]);
        }
        for (int i = 0; i <=255; i++) {
            System.out.print(Integer.toString(ba[i]&0xff,16)+"  ");
            System.out.println(Integer.toString((ba[i]&0xff)+0x100,16));
        }
    }
}

0到f只输出了一位

80  180
81  181
82  182
83  183
84  184
85  185
86  186
87  187
88  188
89  189
8a  18a
8b  18b
8c  18c
8d  18d
8e  18e
8f  18f
90  190
91  191
92  192
93  193
94  194
95  195
96  196
97  197
98  198
99  199
9a  19a
9b  19b
9c  19c
9d  19d
9e  19e
9f  19f
a0  1a0
a1  1a1
a2  1a2
a3  1a3
a4  1a4
a5  1a5
a6  1a6
a7  1a7
a8  1a8
a9  1a9
aa  1aa
ab  1ab
ac  1ac
ad  1ad
ae  1ae
af  1af
b0  1b0
b1  1b1
b2  1b2
b3  1b3
b4  1b4
b5  1b5
b6  1b6
b7  1b7
b8  1b8
b9  1b9
ba  1ba
bb  1bb
bc  1bc
bd  1bd
be  1be
bf  1bf
c0  1c0
c1  1c1
c2  1c2
c3  1c3
c4  1c4
c5  1c5
c6  1c6
c7  1c7
c8  1c8
c9  1c9
ca  1ca
cb  1cb
cc  1cc
cd  1cd
ce  1ce
cf  1cf
d0  1d0
d1  1d1
d2  1d2
d3  1d3
d4  1d4
d5  1d5
d6  1d6
d7  1d7
d8  1d8
d9  1d9
da  1da
db  1db
dc  1dc
dd  1dd
de  1de
df  1df
e0  1e0
e1  1e1
e2  1e2
e3  1e3
e4  1e4
e5  1e5
e6  1e6
e7  1e7
e8  1e8
e9  1e9
ea  1ea
eb  1eb
ec  1ec
ed  1ed
ee  1ee
ef  1ef
f0  1f0
f1  1f1
f2  1f2
f3  1f3
f4  1f4
f5  1f5
f6  1f6
f7  1f7
f8  1f8
f9  1f9
fa  1fa
fb  1fb
fc  1fc
fd  1fd
fe  1fe
ff  1ff
0  100
1  101
2  102
3  103
4  104
5  105
6  106
7  107
8  108
9  109
a  10a
b  10b
c  10c
d  10d
e  10e
f  10f
10  110
11  111
12  112
13  113
14  114
15  115
16  116
17  117
18  118
19  119
1a  11a
1b  11b
1c  11c
1d  11d
1e  11e
1f  11f
20  120
21  121
22  122
23  123
24  124
25  125
26  126
27  127
28  128
29  129
2a  12a
2b  12b
2c  12c
2d  12d
2e  12e
2f  12f
30  130
31  131
32  132
33  133
34  134
35  135
36  136
37  137
38  138
39  139
3a  13a
3b  13b
3c  13c
3d  13d
3e  13e
3f  13f
40  140
41  141
42  142
43  143
44  144
45  145
46  146
47  147
48  148
49  149
4a  14a
4b  14b
4c  14c
4d  14d
4e  14e
4f  14f
50  150
51  151
52  152
53  153
54  154
55  155
56  156
57  157
58  158
59  159
5a  15a
5b  15b
5c  15c
5d  15d
5e  15e
5f  15f
60  160
61  161
62  162
63  163
64  164
65  165
66  166
67  167
68  168
69  169
6a  16a
6b  16b
6c  16c
6d  16d
6e  16e
6f  16f
70  170
71  171
72  172
73  173
74  174
75  175
76  176
77  177
78  178
79  179
7a  17a
7b  17b
7c  17c
7d  17d
7e  17e
7f  17f

回复
阅读 785
4 个回答

&上0xff,是为了转变成int类型了,可以让字节的第八位不被当做符号位。

至于加上0x100,和加上0x200或0x300都是一样的效果,只是为了方便格式化输出,让每个字节都是对应两个字符。不加0x100时,字节1输出字符1。加上0x100后,每个字节输出三个字符串,再经过substring(1)截取后,输出就是字符01。

数字 0 ~ 15 会输出十六进制的 0 ~ F,都只有一个字符。如果想要两位对齐,就需要前面补 0。

问题是,如果原数值大于 15,比如 20,转成十六进制输出是 14,如果前面补 0 就变成了 014。

那么要处理成两位对齐,有几种办法,最直接的是 if 判断

String[] hex = new String[] { "4", "14" };
for (String s : hex) {
    String ss = s.length() == 1 ? "0" + s : s;
    System.out.println(ss);;
}

// 输出 04 和 14

另一种是补 0 再截断的办法:

String[] hex = new String[] {"4", "14"};
for (String s : hex) {
    String ss = ("0" + s).substring(s.length() - 1);
    System.out.println(ss);
}

// 输出 04 和 14

注意这里要截断的是后 2 位,也就是 ss.length() - 2

$$ \because ss.length() = s.length() + 1 \\ \therefore ss.length() - 2 = s.length() + 1 - 2 = s.length() - 1 $$

上面两种办法都是从结果来处理的。

而题主给的办法是从源头来处理的。如果原数字加上了 0x100,那么转成十六进制的结果就一定是 0x1##,长度一定是 3,只需要把第一位截取掉就可以了,也就是 Integer.toHexString(n + 0x100).substring(1)(注意这里 n 是处理过的 int 值,也就是通过 b & 0xff 截取掉所有前导 ff 的值)


忘了 String.format("%02x", b & 0x0ff) 这种方法。

另外,如果用现成的库转 byte[] 会更方法,比如用 Guava

String s = BaseEncoding.base16().encode(bytes)

但似乎离题主的问题有点远了

需要回头去看:java.lang.Integer#toString(int i, int radix)的实现。
实现并不复杂,主要就是递归除取余数,比如:Integer.toString(126,16),那就是不断递归,除余然后按余数取字符数组digits内的字符(可见该函数最大也只支持转换 36 进制表示),除余之后再取相除的结果继续除余,直到商小于除数。

126 % 16 = 7······14      //     [...][E]
7 % 16 = 0······7         //  [...][7][E]

这是刚好有多于 2 位的结果,如果是原本小于 16 的数字,如 15,按Integer#toString(int i, int radix)的实现,只会得到

15 % 16 = 0······15       //     [...][F]

所以在外部进行+ 0x100,使得参数能够大于 16 的 2次幂,得到的结果的字符数也就大于 3 个,最后只需要截取最开始的一位即可。

271 % 16 = 16······15     //       [...][F]
16 % 16 = 1······0        //    [...][0][F]
1 % 16 = 0······1         // [...][1][0][F]

截取后即为:0F。

宣传栏