Hex编码
编码原理
Hex编码就是把一个8位的字节数据用两个十六进制数展示出来,编码时,将8位二进制码重新分组成两个4位的字节,其中一个字节的低4位是原字节的高四位,另一个字节的低4位是原数据的低4位,高4位都补0,然后输出这两个字节对应十六进制数字作为编码。Hex编码后的长度是源数据的2倍,Hex编码的编码表为
0 0 1 1 2 2 3 3
4 4 5 5 6 6 7 7
8 8 9 9 10 a 11 b
12 c 13 d 14 e 15 f
比如ASCII码A的Hex编码过程为
ASCII码:A (65)
二进制码:0100_0001
重新分组:0000_0100 0000_0001
十六进制: 4 1
Hex编码:41
丁
e4b881
代码实现
使用Bouncy Castle的实现
下面的代码使用开源软件Bouncy Castle实现Hex编解码,使用的版本是1.56。
import java.io.UnsupportedEncodingException;
import org.bouncycastle.util.encoders.Hex;
public class HexTestBC {
public static void main(String[] args)
throws UnsupportedEncodingException {
// 编码
byte data[] = "A".getBytes("UTF-8");
byte[] encodeData = Hex.encode(data);
String encodeStr = Hex.toHexString(data);
System.out.println(new String(encodeData, "UTF-8"));
System.out.println(encodeStr);
// 解码
byte[] decodeData = Hex.decode(encodeData);
byte[] decodeData2 = Hex.decode(encodeStr);
System.out.println(new String(decodeData, "UTF-8"));
System.out.println(new String(decodeData2, "UTF-8"));
}
}
程序输出
41
41
A
A
使用Apache Commons Codec实现
下面的代码使用开源软件Apache Commons Codec实现Hex编解码,使用的版本是1.10。
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
public class HexTestCC {
public static void main(String[] args)
throws UnsupportedEncodingException,
DecoderException {
// 编码
byte data[] = "A".getBytes("UTF-8");
char[] encodeData = Hex.encodeHex(data);
String encodeStr = Hex.encodeHexString(data);
System.out.println(new String(encodeData));
System.out.println(encodeStr);
// 解码
byte[] decodeData = Hex.decodeHex(encodeData);
System.out.println(new String(decodeData, "UTF-8"));
}
}
源码分析
Bouncy Castle实现源码分析
Bouncy Castle实现Hex编解码的是org.bouncycastle.util.encoders.HexEncoder类,实现编码时首先定义了一个编码表
protected final byte[] encodingTable =
{
(byte)'0', (byte)'1', (byte)'2', (byte)'3',
(byte)'4', (byte)'5', (byte)'6', (byte)'7',
(byte)'8', (byte)'9', (byte)'a', (byte)'b',
(byte)'c', (byte)'d', (byte)'e', (byte)'f'
};
然后编码的代码是
public int encode(
byte[] data,
int off,
int length,
OutputStream out)
throws IOException
{
for (int i = off; i < (off + length); i++)
{
int v = data[i] & 0xff;
out.write(encodingTable[(v >>> 4)]);
out.write(encodingTable[v & 0xf]);
}
return length * 2;
}
解码的实现稍微复杂一点,在HexEncoder的构造方法中会调用initialiseDecodingTable建立解码表,代码如下
protected final byte[] decodingTable = new byte[128];
protected void initialiseDecodingTable()
{
for (int i = 0; i < decodingTable.length; i++)
{
decodingTable[i] = (byte)0xff;
}
for (int i = 0; i < encodingTable.length; i++)
{
decodingTable[encodingTable[i]] = (byte)i;
}
decodingTable[<span class="hljs-string">'A'</span>] = decodingTable[<span class="hljs-string">'a'</span>];
decodingTable[<span class="hljs-string">'B'</span>] = decodingTable[<span class="hljs-string">'b'</span>];
decodingTable[<span class="hljs-string">'C'</span>] = decodingTable[<span class="hljs-string">'c'</span>];
decodingTable[<span class="hljs-string">'D'</span>] = decodingTable[<span class="hljs-string">'d'</span>];
decodingTable[<span class="hljs-string">'E'</span>] = decodingTable[<span class="hljs-string">'e'</span>];
decodingTable[<span class="hljs-string">'F'</span>] = decodingTable[<span class="hljs-string">'f'</span>];
}
解码表是一个长度是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 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1
-1 ! -1 " -1 # -1 $ -1 % -1 & -1 ' -1
( -1 ) -1 * -1 + -1 , -1 - -1 . -1 / -1
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7
8 8 9 9 : -1 ; -1 < -1 = -1 > -1 ? -1
@ -1 A 10 B 11 C 12 D 13 E 14 F 15 G -1
H -1 I -1 J -1 K -1 L -1 M -1 N -1 O -1
P -1 Q -1 R -1 S -1 T -1 U -1 V -1 W -1
X -1 Y -1 Z -1 [ -1 \ -1 ] -1 ^ -1 _ -1
` -1 a 10 b 11 c 12 d 13 e 14 f 15 g -1
h -1 i -1 j -1 k -1 l -1 m -1 n -1 o -1
p -1 q -1 r -1 s -1 t -1 u -1 v -1 w -1
x -1 y -1 z -1 { -1 | -1 } -1 ~ -1 -1
解码的过程实际上就是获取连续两个字节,取这两个字节解码表中对应的数值,然后将这两个数值拼接成一个8位二进制码,作为解码的输出。源码如下:
public int decode(
byte[] data,
int off,
int length,
OutputStream out)
throws IOException
{
byte b1, b2;
int outLen = 0;
int <span class="hljs-keyword">end</span> = off + length;
<span class="hljs-keyword">while</span> (<span class="hljs-keyword">end</span> > off)
{
<span class="hljs-keyword">if</span> (!ignore((char)data[<span class="hljs-keyword">end</span> - <span class="hljs-number">1</span>]))
{
<span class="hljs-keyword">break</span>;
}
<span class="hljs-keyword">end</span>--;
}
int i = off;
<span class="hljs-keyword">while</span> (i < <span class="hljs-keyword">end</span>)
{
<span class="hljs-keyword">while</span> (i < <span class="hljs-keyword">end</span> && ignore((char)data[i]))
{
i++;
}
b1 = decodingTable[data[i++]];
<span class="hljs-keyword">while</span> (i < <span class="hljs-keyword">end</span> && ignore((char)data[i]))
{
i++;
}
b2 = decodingTable[data[i++]];
<span class="hljs-keyword">if</span> ((b1 <span class="hljs-params">| b2) < 0)
{
throw new IOException("invalid
characters encountered <span class="hljs-keyword">in</span> Hex data");
}
out.write((b1 << 4) |</span> b2);
outLen++;
}
<span class="hljs-keyword">return</span> outLen;
}
其中ignore方法的代码如下,解码时会忽略首、尾及中间的空白。
private static boolean ignore(
char c)
{
return c == '\n' || c =='\r' || c == '\t' || c == ' ';
}
示例代码中的Hex工具类持有HexEncoder的实例,并通过ByteArrayOutputStream类实现对byte数组的操作,此外不再赘述。
public class Hex
{
private static final Encoder encoder = new HexEncoder();
public static byte[] encode(
byte[] data,
int off,
int length)
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
<span class="hljs-keyword">try</span>
{
encoder.encode(data, off, length, bOut);
}
<span class="hljs-keyword">catch</span> (Exception e)
{
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> EncoderException(<span class="hljs-string">"exception encoding Hex string: "</span>
+ e.getMessage(), e);
}
<span class="hljs-keyword">return</span> bOut.toByteArray();
}
......
}
Apache Commons Codec实现源码分析
Apache Commons Codec实现Hex编码的步骤是直接创建一个两倍源数据长度的字符数组,然后分别将源数据的每个字节转换成两个字节放到目标字节数组中,Apache Commons Codec支持设置的要转换为大写还是小写。
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'};
public static char[] encodeHex(final byte[] data) {
return encodeHex(data, true);
}
public static char[] encodeHex(final byte[] data,
final boolean toLowerCase) {
return encodeHex(data,
toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
protected static char[] encodeHex(final byte[] data,
final char[] toDigits) {
final int l = data.length;
final 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;
}
Apache Commons Codec实现Hex解码的步骤是首先创建一个原字符串一半长度的字节数组,然后依次将两个连续的十六进制数转换为一个字节数据,转换时使用了JDK的Character.digit方法。
public static byte[] decodeHex(final char[] data)
throws DecoderException {
final int len = data.length;
if ((len & 0x01) != 0) {
throw new DecoderException("Odd number of characters.");
}
final 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;
}
protected static int toDigit(final char ch, final int index)
throws DecoderException {
final int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new DecoderException(""
+ "Illegal hexadecimal character "
+ ch + " at index " + index);
}
return digit;
}
</div>
</div>
Hex编码的更多相关文章
- Hex编码字节
1.将字节数组转换为字符串 /** * 将字节数组转换为字符串 * 一个字节会形成两个字符,最终长度是原始数据的2倍 * @param data * @return */ public static ...
- 浅谈Hex编码算法
一.什么是Hex 将每一个字节表示的十六进制表示的内容,用字符串来显示. 二.作用 将不可见的,复杂的字节数组数据,转换为可显示的字符串数据 类似于Base64编码算法 区别:Base64将三个字节转 ...
- 普通字符串与Hex编码字符串之间转换
import java.io.UnsupportedEncodingException; import org.apache.commons.codec.binary.Hex; public clas ...
- Hex编码 十六进制编码
import java.io.UnsupportedEncodingException; import java.net.URLEncoder; /** * HEX字符串与字节码(字符串)转换工具 ...
- C# Hex编码和解码
/// 从字符串转换到16进制表示的字符串 /// 编码,如"utf-8","gb2312" /// 是否每字符用逗号分隔 public static stri ...
- 使用hex编码绕过主机卫士IIS版本继续注入
本文作者:非主流 测试文件的源码如下: 我们先直接加上单引号试试: http://192.168.0.20/conn.asp?id=1%27 很好,没有报错.那我们继续,and 1=1 和and 1= ...
- js支持中文的hex编码 bin2hex (utf-8)
背景: 最近对接接口的时候需要将请求参数转为16进制,因此研究了下这个bin2hex.在js中转16进制 使用的是: str.charCodeAt(i).toString(16); 在遇到中文的时候编 ...
- 代码,绘画,设计常用的颜色名称-16进制HEX编码-RGB编码 对照一览表
排列方式,英文名称的字典序 颜色名 HEX16进制编码 RGB编码 AliceBlue F0F8FF 240,248,255 AntiqueWhite FAEBD7 250,235,215 Aqua ...
- 浅谈编码Base64、Hex、UTF-8、Unicode、GBK等
网络上大多精彩的回答,该随笔用作自我总结: 首先计算机只认得二进制,0和1,所以我们现在看到的字都是经过二进制数据编码后的:计算机能针对0和1的组合做很多事情,这些规则都是人定义的:然后有了字节的概念 ...
随机推荐
- Eslint报错的翻译
若在git中出现这个 http://eslint.org/docs/rules/eol-last 他是提醒你:在文件末尾要求或禁止换行 比如代码如下: 若在git中出现这个 https://eslin ...
- 【机器学习PAI实战】—— 玩转人工智能之综述
摘要: 基于人工智能火热的大背景下,通过阿里云的机器学习平台PAI在真实场景中的应用,详细阐述相关算法及使用方法,力求能够让读者读后能够马上动手利用PAI搭建属于自己的机器学习实用方案,真正利用PAI ...
- 【JZOJ3886】【长郡NOIP2014模拟10.22】道路维护
CCC 最近徆多人投诉说C国的道路破损程度太大,以至亍无法通行 C国的政府徆重视这件事,但是最近财政有点紧,丌可能将所有的道路都进行维护,所以他们决定按照下述方案进行维护 将C国抽象成一个无向图,定义 ...
- 【JZOJ4782】【NOIP2016提高A组模拟9.15】Math
题目描述 输入 输出 样例输入 3 5 样例输出 -1 数据范围 解法 观察式子,可以得知整个式子与d(i*j)的奇偶性有关. d(n)为奇数当且仅当n是完全平方数. 对于一个i,如果d(i*j) ( ...
- install4j的使用
用java写好了桌面应用,怎么搞成 那种常见的 双击之后 next.next...安装完成的按照包呢?用install4j.这东西有多好用呢?看看这款xml编辑软件,就是用install4j封装的安装 ...
- JavaScript中的this关键字的几种用法
JS 里的 this 在 function 内部被创建 指向调用时所在函数所绑定的对象(拗口) this 不能被赋值,但可以被 call/apply 改变 1. this 和构造函数 function ...
- 解决VS+Qt不生成moc文件问题
使用VS的Qt插件进行Qt开发时,有时候会遇到不能生成moc文件的问题. 1.在工程中可以看到这个Generated files目录下是有一个看似moc文件的文件,双击打开的话: 如果能正常打开,文件 ...
- BZOJ 1500 洛谷2042维护序列题解
BZ链接 洛谷链接 这道题真是丧心病狂.... 应该很容易就可以看出做法,但是写代码写的....... 思路很简单,用一个平衡树维护一下所有的操作就好了,重点讲解一下代码的细节 首先如果按照常规写法的 ...
- 强制去除xcode的编译警告
使用 #pragma clang diagnostic ignored 语法来强制去除xcode的编译警告,代码举例如下: #pragma clang diagnostic push #pragma ...
- sql —— like
用于在 WHERE 子句中搜索列中的指定模式. 原表: 一.% %表示任何字符出现任意次数. 1.以某个字符串开头的数据 2.包含某个字符串的数据 3.以某个字符串结尾的数据 二._ 只适用于匹配单个 ...