编码原理

Hex编码就是把一个8位的字节数据用两个十六进制数展示出来,编码时,将8位二进制码重新分组成两个4位的字节,其中一个字节的低4位是原字节的高四位,另一个字节的低4位是原数据的低4位,高4位都补0,然后输出这两个字节对应十六进制数字作为编码。Hex编码后的长度是源数据的2倍,Hex编码的编码表为

  1. 0 0 1 1 2 2 3 3
  2. 4 4 5 5 6 6 7 7
  3. 8 8 9 9 10 a 11 b
  4. 12 c 13 d 14 e 15 f

比如ASCII码A的Hex编码过程为

  1. ASCII码:A (65)
  2. 二进制码:0100_0001
  3. 重新分组:0000_0100 0000_0001
  4. 十六进制: 4 1
  5. Hex编码:41

  1. e4b881

代码实现

使用Bouncy Castle的实现

下面的代码使用开源软件Bouncy Castle实现Hex编解码,使用的版本是1.56。

  1. import java.io.UnsupportedEncodingException;
  2. import org.bouncycastle.util.encoders.Hex;
  3. public class HexTestBC {
  4. public static void main(String[] args)
  5. throws UnsupportedEncodingException {
  6. // 编码
  7. byte data[] = "A".getBytes("UTF-8");
  8. byte[] encodeData = Hex.encode(data);
  9. String encodeStr = Hex.toHexString(data);
  10. System.out.println(new String(encodeData, "UTF-8"));
  11. System.out.println(encodeStr);
  12. // 解码
  13. byte[] decodeData = Hex.decode(encodeData);
  14. byte[] decodeData2 = Hex.decode(encodeStr);
  15. System.out.println(new String(decodeData, "UTF-8"));
  16. System.out.println(new String(decodeData2, "UTF-8"));
  17. }
  18. }

程序输出

  1. 41
  2. 41
  3. A
  4. A

使用Apache Commons Codec实现

下面的代码使用开源软件Apache Commons Codec实现Hex编解码,使用的版本是1.10。

  1. import java.io.UnsupportedEncodingException;
  2. import org.apache.commons.codec.DecoderException;
  3. import org.apache.commons.codec.binary.Hex;
  4. public class HexTestCC {
  5. public static void main(String[] args)
  6. throws UnsupportedEncodingException,
  7. DecoderException {
  8. // 编码
  9. byte data[] = "A".getBytes("UTF-8");
  10. char[] encodeData = Hex.encodeHex(data);
  11. String encodeStr = Hex.encodeHexString(data);
  12. System.out.println(new String(encodeData));
  13. System.out.println(encodeStr);
  14. // 解码
  15. byte[] decodeData = Hex.decodeHex(encodeData);
  16. System.out.println(new String(decodeData, "UTF-8"));
  17. }
  18. }

源码分析

Bouncy Castle实现源码分析

Bouncy Castle实现Hex编解码的是org.bouncycastle.util.encoders.HexEncoder类,实现编码时首先定义了一个编码表

  1. protected final byte[] encodingTable =
  2. {
  3. (byte)'0', (byte)'1', (byte)'2', (byte)'3',
  4. (byte)'4', (byte)'5', (byte)'6', (byte)'7',
  5. (byte)'8', (byte)'9', (byte)'a', (byte)'b',
  6. (byte)'c', (byte)'d', (byte)'e', (byte)'f'
  7. };

然后编码的代码是

  1. public int encode(
  2. byte[] data,
  3. int off,
  4. int length,
  5. OutputStream out)
  6. throws IOException
  7. {
  8. for (int i = off; i < (off + length); i++)
  9. {
  10. int v = data[i] & 0xff;
  11. out.write(encodingTable[(v >>> 4)]);
  12. out.write(encodingTable[v & 0xf]);
  13. }
  14. return length * 2;
  15. }

解码的实现稍微复杂一点,在HexEncoder的构造方法中会调用initialiseDecodingTable建立解码表,代码如下

  1. protected final byte[] decodingTable = new byte[128];
  2. protected void initialiseDecodingTable()
  3. {
  4. for (int i = 0; i < decodingTable.length; i++)
  5. {
  6. decodingTable[i] = (byte)0xff;
  7. }
  8. for (int i = 0; i < encodingTable.length; i++)
  9. {
  10. decodingTable[encodingTable[i]] = (byte)i;
  11. }
  12. decodingTable[<span class="hljs-string">'A'</span>] = decodingTable[<span class="hljs-string">'a'</span>];
  13. decodingTable[<span class="hljs-string">'B'</span>] = decodingTable[<span class="hljs-string">'b'</span>];
  14. decodingTable[<span class="hljs-string">'C'</span>] = decodingTable[<span class="hljs-string">'c'</span>];
  15. decodingTable[<span class="hljs-string">'D'</span>] = decodingTable[<span class="hljs-string">'d'</span>];
  16. decodingTable[<span class="hljs-string">'E'</span>] = decodingTable[<span class="hljs-string">'e'</span>];
  17. decodingTable[<span class="hljs-string">'F'</span>] = decodingTable[<span class="hljs-string">'f'</span>];
  18. }

解码表是一个长度是128的字节数组,每个位置代表对应的ASCII码,该位置上的值表示该ASCII码对应的二进制码。具体到Hex的解码表,第48-59个位置,即ASCII码0-9的位置保存了数字0-9,第65-70个位置,即ASCII码A-F的位置保存了数字10-15,第97-102个位置,即ASCII码a-f同样保存了数字10-15。解码表为

比如array[65] = A

  1. -1 -1 -1 -1 -1 -1 -1 -1
  2. -1 -1 -1 -1 -1 -1 -1 -1
  3. -1 -1 -1 -1 -1 -1 -1 -1
  4. -1 -1 -1 -1 -1 -1 -1 -1
  5. -1 ! -1 " -1 # -1 $ -1 % -1 & -1 ' -1
  6. ( -1 ) -1 * -1 + -1 , -1 - -1 . -1 / -1
  7. 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7
  8. 8 8 9 9 : -1 ; -1 < -1 = -1 > -1 ? -1
  9. @ -1 A 10 B 11 C 12 D 13 E 14 F 15 G -1
  10. H -1 I -1 J -1 K -1 L -1 M -1 N -1 O -1
  11. P -1 Q -1 R -1 S -1 T -1 U -1 V -1 W -1
  12. X -1 Y -1 Z -1 [ -1 \ -1 ] -1 ^ -1 _ -1
  13. ` -1 a 10 b 11 c 12 d 13 e 14 f 15 g -1
  14. h -1 i -1 j -1 k -1 l -1 m -1 n -1 o -1
  15. p -1 q -1 r -1 s -1 t -1 u -1 v -1 w -1
  16. x -1 y -1 z -1 { -1 | -1 } -1 ~ -1 -1

解码的过程实际上就是获取连续两个字节,取这两个字节解码表中对应的数值,然后将这两个数值拼接成一个8位二进制码,作为解码的输出。源码如下:

  1. public int decode(
  2. byte[] data,
  3. int off,
  4. int length,
  5. OutputStream out)
  6. throws IOException
  7. {
  8. byte b1, b2;
  9. int outLen = 0;
  10. int     <span class="hljs-keyword">end</span> = off + length;
  11. <span class="hljs-keyword">while</span> (<span class="hljs-keyword">end</span> &gt; off)
  12. {
  13.     <span class="hljs-keyword">if</span> (!ignore((char)data[<span class="hljs-keyword">end</span> - <span class="hljs-number">1</span>]))
  14.     {
  15.         <span class="hljs-keyword">break</span>;
  16.     }
  17.     <span class="hljs-keyword">end</span>--;
  18. }
  19. int i = off;
  20. <span class="hljs-keyword">while</span> (i &lt; <span class="hljs-keyword">end</span>)
  21. {
  22.     <span class="hljs-keyword">while</span> (i &lt; <span class="hljs-keyword">end</span> &amp;&amp; ignore((char)data[i]))
  23.     {
  24.         i++;
  25.     }
  26.     b1 = decodingTable[data[i++]];
  27.     <span class="hljs-keyword">while</span> (i &lt; <span class="hljs-keyword">end</span> &amp;&amp; ignore((char)data[i]))
  28.     {
  29.         i++;
  30.     }
  31.     b2 = decodingTable[data[i++]];
  32.     <span class="hljs-keyword">if</span> ((b1 <span class="hljs-params">| b2) &lt; 0)
  33.     {
  34.         throw new IOException("invalid
  35.               characters encountered <span class="hljs-keyword">in</span> Hex data");
  36.     }
  37.     out.write((b1 &lt;&lt; 4) |</span> b2);
  38.     outLen++;
  39. }
  40. <span class="hljs-keyword">return</span> outLen;
  41. }

其中ignore方法的代码如下,解码时会忽略首、尾及中间的空白。

  1. private static boolean ignore(
  2. char c)
  3. {
  4. return c == '\n' || c =='\r' || c == '\t' || c == ' ';
  5. }

示例代码中的Hex工具类持有HexEncoder的实例,并通过ByteArrayOutputStream类实现对byte数组的操作,此外不再赘述。

  1. public class Hex
  2. {
  3. private static final Encoder encoder = new HexEncoder();
  4. public static byte[] encode(
  5. byte[] data,
  6. int off,
  7. int length)
  8. {
  9. ByteArrayOutputStream bOut = new ByteArrayOutputStream();
  10.     <span class="hljs-keyword">try</span>
  11.     {
  12.         encoder.encode(data, off, length, bOut);
  13.     }
  14.     <span class="hljs-keyword">catch</span> (Exception e)
  15.     {
  16.         <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> EncoderException(<span class="hljs-string">"exception encoding Hex string: "</span>
  17.                   + e.getMessage(), e);
  18.     }
  19.     <span class="hljs-keyword">return</span> bOut.toByteArray();
  20. }
  21. ......
  22. }

Apache Commons Codec实现源码分析

Apache Commons Codec实现Hex编码的步骤是直接创建一个两倍源数据长度的字符数组,然后分别将源数据的每个字节转换成两个字节放到目标字节数组中,Apache Commons Codec支持设置的要转换为大写还是小写。

  1. private static final char[] DIGITS_LOWER =
  2. {'0', '1', '2', '3', '4', '5', '6', '7',
  3. '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  4. private static final char[] DIGITS_UPPER =
  5. {'0', '1', '2', '3', '4', '5', '6', '7',
  6. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  7. public static char[] encodeHex(final byte[] data) {
  8. return encodeHex(data, true);
  9. }
  10. public static char[] encodeHex(final byte[] data,
  11. final boolean toLowerCase) {
  12. return encodeHex(data,
  13. toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
  14. }
  15. protected static char[] encodeHex(final byte[] data,
  16. final char[] toDigits) {
  17. final int l = data.length;
  18. final char[] out = new char[l << 1];
  19. // two characters form the hex value.
  20. for (int i = 0, j = 0; i < l; i++) {
  21. out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
  22. out[j++] = toDigits[0x0F & data[i]];
  23. }
  24. return out;
  25. }

Apache Commons Codec实现Hex解码的步骤是首先创建一个原字符串一半长度的字节数组,然后依次将两个连续的十六进制数转换为一个字节数据,转换时使用了JDK的Character.digit方法。

  1. public static byte[] decodeHex(final char[] data)
  2. throws DecoderException {
  3. final int len = data.length;
  4. if ((len & 0x01) != 0) {
  5. throw new DecoderException("Odd number of characters.");
  6. }
  7. final byte[] out = new byte[len >> 1];
  8. // two characters form the hex value.
  9. for (int i = 0, j = 0; j < len; i++) {
  10. int f = toDigit(data[j], j) << 4;
  11. j++;
  12. f = f | toDigit(data[j], j);
  13. j++;
  14. out[i] = (byte) (f & 0xFF);
  15. }
  16. return out;
  17. }
  18. protected static int toDigit(final char ch, final int index)
  19. throws DecoderException {
  20. final int digit = Character.digit(ch, 16);
  21. if (digit == -1) {
  22. throw new DecoderException(""
  23. + "Illegal hexadecimal character "
  24. + ch + " at index " + index);
  25. }
  26. return digit;
  27. }
  1. </div>
  2. </div>

原文地址:https://www.jianshu.com/p/57c4e8d3f035

posted @
2019-06-12 16:49 
星朝 
阅读(...) 
评论(...) 
编辑 
收藏

Hex编码的更多相关文章

  1. Hex编码字节

    1.将字节数组转换为字符串 /** * 将字节数组转换为字符串 * 一个字节会形成两个字符,最终长度是原始数据的2倍 * @param data * @return */ public static ...

  2. 浅谈Hex编码算法

    一.什么是Hex 将每一个字节表示的十六进制表示的内容,用字符串来显示. 二.作用 将不可见的,复杂的字节数组数据,转换为可显示的字符串数据 类似于Base64编码算法 区别:Base64将三个字节转 ...

  3. 普通字符串与Hex编码字符串之间转换

    import java.io.UnsupportedEncodingException; import org.apache.commons.codec.binary.Hex; public clas ...

  4. Hex编码 十六进制编码

    import java.io.UnsupportedEncodingException; import java.net.URLEncoder; /**  * HEX字符串与字节码(字符串)转换工具 ...

  5. C# Hex编码和解码

    /// 从字符串转换到16进制表示的字符串 /// 编码,如"utf-8","gb2312" /// 是否每字符用逗号分隔 public static stri ...

  6. 使用hex编码绕过主机卫士IIS版本继续注入

    本文作者:非主流 测试文件的源码如下: 我们先直接加上单引号试试: http://192.168.0.20/conn.asp?id=1%27 很好,没有报错.那我们继续,and 1=1 和and 1= ...

  7. js支持中文的hex编码 bin2hex (utf-8)

    背景: 最近对接接口的时候需要将请求参数转为16进制,因此研究了下这个bin2hex.在js中转16进制 使用的是: str.charCodeAt(i).toString(16); 在遇到中文的时候编 ...

  8. 代码,绘画,设计常用的颜色名称-16进制HEX编码-RGB编码 对照一览表

    排列方式,英文名称的字典序 颜色名 HEX16进制编码 RGB编码 AliceBlue F0F8FF 240,248,255 AntiqueWhite FAEBD7 250,235,215 Aqua ...

  9. 浅谈编码Base64、Hex、UTF-8、Unicode、GBK等

    网络上大多精彩的回答,该随笔用作自我总结: 首先计算机只认得二进制,0和1,所以我们现在看到的字都是经过二进制数据编码后的:计算机能针对0和1的组合做很多事情,这些规则都是人定义的:然后有了字节的概念 ...

随机推荐

  1. HTML-DOM实例——实现带样式的表单验证

        HTML样式 基于table标签来实现页面结构 <form id="form1"> <h2>增加管理员</h2> <table&g ...

  2. Mathcad 是一种工程计算软件,主要运算功能:代数运算、线性代数、微积分、符号计算、2D和3D图表、动画、函数、程序编写、逻辑运算、变量与单位的定义和计算等。

    Mathcad软件包Mathcad是由MathSoft公司(2006 年4 月被美国PTC收购)推出的一种交互式数值计算系统. Mathcad 是一种工程计算软件,作为工程计算的全球标准,与专有的计算 ...

  3. TRS OM error

    http://192.168.1.1/ http://tplogin.cn/admin888 wddqaz123456789 package="com.trs.om.bean" m ...

  4. 【JZOJ3885】【长郡NOIP2014模拟10.22】搞笑的代码

    ok 在OI界存在着一位传奇选手--QQ,他总是以风格迥异的搞笑代码受世人围观 某次某道题目的输入是一个排列,他使用了以下伪代码来生成数据 while 序列长度<n do { 随机生成一个整数属 ...

  5. Python对于封装性的看法

  6. PLAY2.6-SCALA(五) Action的组合、范围的设置以及错误的处理

    一.自定义action 从一个日志装饰器的例子开始 1.在invokeBlock方法中实现 import play.api.mvc._ class LoggingAction @Inject() (p ...

  7. 错误信息:FATAL: No bootable medium found! System halted.

    一.解决方法 先上1张图,显示的错误信息 再上2张图,幸好在之前安装了XP系统,不然还真不好解决.从图中可以看出WIN-XP和Linux系统安装好之后的差异,Linux的的存储信息上显示“第二通道没有 ...

  8. Oracle函数——MINUS

    解释 “minus”直接翻译为中文是“减”的意思,在Oracle中也是用来做减法操作的,只不过它不是传统意义上对数字的减法,而是对查询结果集的减法.A minus B就意味着将结果集A去除结果集B中所 ...

  9. 【JZOJ4877】【NOIP2016提高A组集训第10场11.8】力场护盾

    题目描述 ZMiG成功粉碎了707的基因突变计划,为了人类的安全,他决定向707的科学实验室发起进攻!707并没有想到有人敢攻击她的实验室,一时间不知所措,决定牺牲电力来换取自己实验室的平安. 在实验 ...

  10. 【JZOJ4833】【NOIP2016提高A组集训第3场10.31】Mahjong

    题目描述 解法 搜索. 代码 #include<stdio.h> #include<iostream> #include<string.h> #include< ...