十六进制(简写为hex或下标16)在数学中是一种逢16进1的进位制,一般用数字0到9和字母A到F表示(其中:A~F即10~15)。
例如十进制数57,在二进制写作111001,在16进制写作39。
像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20
十六进制的每个字符需要用4位二进制位来表示
2的四次方等于16。所以要用4个二进制位表示一个十六进制字符
如下所示:
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0011 |
4 | 0100 |
5 | 0101 |
6 | 0110 |
7 | 0111 |
8 | 1000 |
9 | 1001 |
A | 1010 |
B | 1011 |
C | 1100 |
D | 1101 |
E | 1110 |
F | 1111 |
Java中byte用二进制表示占用8位,而我们知道16进制的每个字符需要用4位二进制位来表示。
所以我们就可以把每个byte转换成两个相应的16进制字符,即把byte的高4位和低4位分别转换成相应的16进制字符H和L,并组合起来得到byte转换到16进制字符串的结果
new String(H) + new String(L)。
同理,相反的转换也是将两个16进制字符转换成一个byte,原理同上。
根据以上原理,我们就可以将byte[] 数组转换为16进制字符串了,当然也可以将16进制字符串转换为byte[]数组了。
Java代码:
package test; public class Hex { /** * 用于建立十六进制字符的输出的小写字符数组 */ private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** * 用于建立十六进制字符的输出的大写字符数组 */ private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @return 十六进制char[] */ public static char[] encodeHex(byte[] data) { return encodeHex(data, true); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式 * @return 十六进制char[] */ public static char[] encodeHex(byte[] data, boolean toLowerCase) { return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制char[] */ protected static char[] encodeHex(byte[] data, char[] toDigits) { int l = data.length; char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return out; } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @return 十六进制String */ public static String encodeHexStr(byte[] data) { return encodeHexStr(data, true); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式 * @return 十六进制String */ public static String encodeHexStr(byte[] data, boolean toLowerCase) { return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制String */ protected static String encodeHexStr(byte[] data, char[] toDigits) { return new String(encodeHex(data, toDigits)); } /** * 将十六进制字符数组转换为字节数组 * * @param data 十六进制char[] * @return byte[] * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 */ public static byte[] decodeHex(char[] data) { int len = data.length; if ((len & 0x01) != 0) { throw new RuntimeException("Odd number of characters."); } byte[] out = new byte[len >> 1]; // two characters form the hex value. for (int i = 0, j = 0; j < len; i++) { int f = toDigit(data[j], j) << 4; j++; f = f | toDigit(data[j], j); j++; out[i] = (byte) (f & 0xFF); } return out; } /** * 将十六进制字符转换成一个整数 * * @param ch 十六进制char * @param index 十六进制字符在字符数组中的位置 * @return 一个整数 * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 */ protected static int toDigit(char ch, int index) { int digit = Character.digit(ch, 16); if (digit == -1) { throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); } return digit; } public static void main(String[] args) { String srcStr = "qwer"; String encodeStr = encodeHexStr(srcStr.getBytes()); String decodeStr = new String(decodeHex(encodeStr.toCharArray())); System.out.println("before encode:" + srcStr); System.out.println("after encode:" + encodeStr); System.out.println("convert:" + decodeStr); } }
示例:将十六进制字符串存为字节数组,可以节省存储空间0499AFA3432E9F2EBD81C134C1F5E4B3(这是一个MD5串)
如果把这个MD5串直接存为字符串,就是32个字节(byte),就是256二进制位。
如果把MD5串的每个字符用16进制字符来表示,那么用二进制位就会表示成4个二进制位,总共是128位,也就是16个字节。那么节省了一半的存储空间。
一个字节可以表示两个16进制字符。
import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; /** * Created with IntelliJ IDEA. * User: ASUS * Date: 14-6-27 * Time: 上午2:02 * To change this template use File | Settings | File Templates. */ public class TestGG { public static byte[] charsToBytes(char[] src) { CharBuffer charBuffer = CharBuffer.allocate(src.length); charBuffer.put(src); charBuffer.flip(); Charset cs = Charset.defaultCharset(); System.out.println(cs.name()); ByteBuffer byteBuffer = cs.encode(charBuffer); return byteBuffer.array(); } public static void main(String args[]) throws Exception{ String md5 = "0499AFA3432E9F2EBD81C134C1F5E4B3"; System.out.println(md5); System.out.println(md5.length()); //32 System.out.println(md5.getBytes().length); //32 System.out.println(md5.toCharArray().length); //32 System.out.println(charsToBytes(md5.toCharArray()).length); //64 String hello = "测试字符测试字符测试字符测试字符测试字符测试字符测试字符测试字符"; System.out.println(hello); System.out.println(hello.length()); //32 System.out.println(hello.getBytes().length); //64 System.out.println(hello.toCharArray().length);//32 System.out.println(charsToBytes(hello.toCharArray()).length);//64 } }
运行结果:(环境,windows,jdk1.7)
E:\test-java>javac -encoding utf-8 TestGG.java
E:\test-java>java TestGG
0499AFA3432E9F2EBD81C134C1F5E4B3
32
32
32
GBK
64
测试字符测试字符测试字符测试字符测试字符测试字符测试字符测试字符
32
64
32
GBK
64
E:\test-java>
补充:
MD5串全是字母和数字,都可以用ASCII码来表示,即一个字节,所以其字节数组的长度是32。
汉字的字符串——汉字是用两个字节编码的,所以其字节数组的长度是64
在Java中,字符数组转换成字节数组,如果字符是用GBK编码,那么字节数组的长度是字符数组的两倍。
同理,如果字符是ASCII编码,那么一个字符就是一个字节的长度,也就是说字节数组和长度和字符数组的长度一样。
看代码,如果这个函数的内容是这样:
public static byte[] charsToBytes(char[] src) { CharBuffer charBuffer = CharBuffer.allocate(src.length); charBuffer.put(src); charBuffer.flip(); Charset cs = Charset.forName("ASCII"); System.out.println(cs.name()); ByteBuffer byteBuffer = cs.encode(charBuffer); return byteBuffer.array(); }
那么上面main函数的结果是这样的:
E:\test-java>java TestGG
0499AFA3432E9F2EBD81C134C1F5E4B3
32
32
32
US-ASCII
32
测试字符测试字符测试字符测试字符测试字符测试字符测试字符测试字符
32
64
32
US-ASCII
32
下面的代码是把hexString存为字节数组
public static byte[] hexStringToBytes(String hexString) { if (hexString == null || hexString.equals("")) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d; } public static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } @Test public void test8() { String md5 = "0499AFA3432E9F2EBD81C134C1F5E4B3"; System.out.println(md5.getBytes().length); System.out.println(hexStringToBytes(md5).length); }
运行结果:
32
16
====END====