1-Hex編碼

編碼原理

Hex編碼就是把一個8位的字節(jié)數(shù)據(jù)用兩個十六進制數(shù)展示出來,編碼時氧猬,將8位二進制碼重新分組成兩個4位的字節(jié)壁涎,其中一個字節(jié)的低4位是原字節(jié)的高四位,另一個字節(jié)的低4位是原數(shù)據(jù)的低4位山林,高4位都補0房待,然后輸出這兩個字節(jié)對應十六進制數(shù)字作為編碼。Hex編碼后的長度是源數(shù)據(jù)的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

代碼實現(xiàn)

使用Bouncy Castle的實現(xiàn)

下面的代碼使用開源軟件Bouncy Castle實現(xiàn)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實現(xiàn)

下面的代碼使用開源軟件Apache Commons Codec實現(xiàn)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實現(xiàn)源碼分析

Bouncy Castle實現(xiàn)Hex編解碼的是org.bouncycastle.util.encoders.HexEncoder類,實現(xiàn)編碼時首先定義了一個編碼表

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;
}

解碼的實現(xiàn)稍微復雜一點明也,在HexEncoder的構(gòu)造方法中會調(diào)用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['A'] = decodingTable['a'];
    decodingTable['B'] = decodingTable['b'];
    decodingTable['C'] = decodingTable['c'];
    decodingTable['D'] = decodingTable['d'];
    decodingTable['E'] = decodingTable['e'];
    decodingTable['F'] = decodingTable['f'];
}

解碼表是一個長度是128的字節(jié)數(shù)組,每個位置代表對應的ASCII碼温数,該位置上的值表示該ASCII碼對應的二進制碼绣硝。具體到Hex的解碼表,第48-59個位置撑刺,即ASCII碼0-9的位置保存了數(shù)字0-9鹉胖,第65-70個位置,即ASCII碼A-F的位置保存了數(shù)字10-15,第97-102個位置甫菠,即ASCII碼a-f同樣保存了數(shù)字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

解碼的過程實際上就是獲取連續(xù)兩個字節(jié),取這兩個字節(jié)解碼表中對應的數(shù)值淑蔚,然后將這兩個數(shù)值拼接成一個8位二進制碼,作為解碼的輸出愕撰。源碼如下:

public int decode(
    byte[]          data,
    int             off,
    int             length,
    OutputStream    out)
    throws IOException
{
    byte    b1, b2;
    int     outLen = 0;
    
    int     end = off + length;
    
    while (end > off)
    {
        if (!ignore((char)data[end - 1]))
        {
            break;
        }
        
        end--;
    }
    
    int i = off;
    while (i < end)
    {
        while (i < end && ignore((char)data[i]))
        {
            i++;
        }
        
        b1 = decodingTable[data[i++]];
        
        while (i < end && ignore((char)data[i]))
        {
            i++;
        }
        
        b2 = decodingTable[data[i++]];
        if ((b1 | b2) < 0)
        {
            throw new IOException("invalid 
                  characters encountered in Hex data");
        }
        out.write((b1 << 4) | b2);
        
        outLen++;
    }
    return outLen;
}

其中ignore方法的代碼如下刹衫,解碼時會忽略首、尾及中間的空白搞挣。

private static boolean ignore(
    char    c)
{
    return c == '\n' || c =='\r' || c == '\t' || c == ' ';
}

示例代碼中的Hex工具類持有HexEncoder的實例带迟,并通過ByteArrayOutputStream類實現(xiàn)對byte數(shù)組的操作,此外不再贅述囱桨。

public class Hex
{
    private static final Encoder encoder = new HexEncoder();
    public static byte[] encode(
        byte[]    data,
        int       off,
        int       length)
    {
        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
        
        try
        {
            encoder.encode(data, off, length, bOut);
        }
        catch (Exception e)
        {
            throw new EncoderException("exception encoding Hex string: " 
                      + e.getMessage(), e);
        }
        
        return bOut.toByteArray();
    }
    ......
}

Apache Commons Codec實現(xiàn)源碼分析

Apache Commons Codec實現(xiàn)Hex編碼的步驟是直接創(chuàng)建一個兩倍源數(shù)據(jù)長度的字符數(shù)組仓犬,然后分別將源數(shù)據(jù)的每個字節(jié)轉(zhuǎn)換成兩個字節(jié)放到目標字節(jié)數(shù)組中,Apache Commons Codec支持設(shè)置的要轉(zhuǎn)換為大寫還是小寫舍肠。

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實現(xiàn)Hex解碼的步驟是首先創(chuàng)建一個原字符串一半長度的字節(jié)數(shù)組搀继,然后依次將兩個連續(xù)的十六進制數(shù)轉(zhuǎn)換為一個字節(jié)數(shù)據(jù),轉(zhuǎn)換時使用了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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叽躯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子肌括,更是在濱河造成了極大的恐慌点骑,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谍夭,死亡現(xiàn)場離奇詭異黑滴,居然都是意外死亡,警方通過查閱死者的電腦和手機紧索,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門袁辈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人齐板,你說我怎么就攤上這事吵瞻。” “怎么了甘磨?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵橡羞,是天一觀的道長。 經(jīng)常有香客問我济舆,道長卿泽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮签夭,結(jié)果婚禮上齐邦,老公的妹妹穿的比我還像新娘。我一直安慰自己第租,他們只是感情好措拇,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著慎宾,像睡著了一般丐吓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趟据,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天券犁,我揣著相機與錄音,去河邊找鬼汹碱。 笑死粘衬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的咳促。 我是一名探鬼主播稚新,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼跪腹!你這毒婦竟也來了枷莉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤尺迂,失蹤者是張志新(化名)和其女友劉穎笤妙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體噪裕,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蹲盘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了膳音。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片召衔。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖祭陷,靈堂內(nèi)的尸體忽然破棺而出苍凛,到底是詐尸還是另有隱情,我是刑警寧澤兵志,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布醇蝴,位于F島的核電站,受9級特大地震影響想罕,放射性物質(zhì)發(fā)生泄漏悠栓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惭适。 院中可真熱鬧笙瑟,春花似錦、人聲如沸癞志。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凄杯。三九已至师溅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盾舌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工蘸鲸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妖谴,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓酌摇,卻偏偏與公主長得像膝舅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子窑多,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容