天天看點

編解碼:Base64編解碼

編解碼:Base64編解碼

編解碼:十六進制編解碼中提到,編解碼本質上是以不同的資料形式來展示“資訊”,可以用二進制來表示,可以用十六進制來表示。

二進制的資料,在計算機中通常是不可讀、不可列印的。

那有沒有一種方式,可以讓二進制的資料變成“可讀可列印”的?

方法有很多,最常見的是十六進制編解碼和Base64編解碼。

十六進制編解碼之前已介紹過,本文介紹Base64編解碼。

百度百科:Base64是一種基于64個可列印字元來表示二進制資料的方法。

Base64編碼要求把3個8位位元組(3*8=24)轉化為4個6位的位元組(4*6=24),之後在6位的前面補兩個0,形成8位一個位元組的形式。 如果剩下的字元不足3個位元組,則用0填充,輸出字元使用’=’,是以編碼後輸出的文本末尾可能會出現1或2個’=’(隻可能出現0/1/2個等号,否則就是錯誤的)。

為啥隻可能出現0/1/2個等号?動手算算就知道。

将所有的bit位用8n表示(n表示待編碼資料位元組數量),則當:

n=1時,8*1 mod 6 = 2bit,需要補2個等号

n=2時,8*2 mod 6 = 4bit,需要補1個等号

n=3時,8*3 mod 6 = 0bit,需要補0個等号

n=4時,8*4 mod 6 = 2bit,需要補2個等号

……

Base64編解碼前後空間大小變化:

6位元組的資料,在編碼後占用8位元組空間;空間多占用了2B,比原來大:2B/6B=1/3

8位元組的資料,在解碼後占用6位元組空間;空間少占用了2B,比原來小:2B/8B=1/4

Base64編碼使得待編碼資料增大,增加到原大小的4/3;

Base64解碼使得戴潔馬資料減小,減小到原大小的3/4;

Demo:

待轉換資料(3位元組24bit):0x61(a), 0x62(b), 0x63(c):

二進制形式:01100001 01100010 01100011

分組(每6bit一組*4組):011000 010110 001001 100011

每組高位補兩個bit的0:00011000 00010110 00001001 00100011

十進制:24 22 9 35

參照對照表(摘自:http://base64.xpcha.com/):

編解碼:Base64編解碼

Base64結果:YWJj

待轉換資料(1位元組8bit):0x61(a)

二進制形式:01100001

不是6bit整數,需要多補4個bit 0,結果為:01100001 0000

分組:011000 010000

高位補兩個bit的0:00011000 00010000

十進制:24 16

轉換結果:Y Q

由于Base64規定,Base64結果長度一定是4的倍數,如果不夠則以’=’補全,則實際Base64結果為:

YQ==

Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static const unsigned char *base64=(unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

/*-----------------------------------------------------------------------------
 * 函數名稱:Base64Encode
 * 功能描述:Base64編碼
 * 參數說明:base64code:編碼結果緩沖區;src:待編碼資料;src_len待編碼資料長度
 * 返 回 值:編碼結果長度(位元組)
 * 備    注:注意留有足夠的結果緩沖區(src_len/3*4)
 * */
static int Base64Encode(char *base64code, const char *src, int src_len)
{
    int n,buflen,i,j;

    if (src_len == )
        src_len = strlen(src);

    buflen=n=src_len;

    for (i=,j=; i<=buflen-; i+=,j+=)
    {
        base64code[j] = (src[i]&)>>;
        base64code[j+] = ((src[i]&)<<) + ((src[i+]&)>>);
        base64code[j+] = ((src[i+]&)<<) + ((src[i+]&)>>);
        base64code[j+] = src[i+]&;
    }

    if (n%==)
    {
        base64code[j] = (src[i]&)>>;
        base64code[j+] = ((src[i]&)<<);
        base64code[j+] = ;
        base64code[j+] = ;
        j += ;
    }
    else if (n%==)
    {
        base64code[j] = (src[i]&)>>;
        base64code[j+] = ((src[i]&)<<) + ((src[i+]&)>>);
        base64code[j+] = ((src[i+]&)<<);
        base64code[j+] = ;
        j += ;
    }

    for (i=; i<j; i++)
        base64code[i] = base64[(int)base64code[i]];

    base64code[j] = ;

    return j;
}

/*-------------------------------------------------------------------------------
 * 函數名稱:Base64Decode
 * 功能描述:Base64解碼
 * 參數說明:buf:解碼結果緩沖區;base64code:待解碼資料;src_len:待解碼資料長度
 * 返 回 值:解碼結果長度(位元組)
 * 備    注:注意,buf中解碼結果緩沖區中資料不一定可列印
 * */
static int Base64Decode(char *buf, const char *base64code, int src_len)
{
    int n,i,j,pad;

    if (src_len == )
        src_len = strlen(base64code);

    if (src_len% != )
        return -;

    unsigned char *p=;
    unsigned char *src=;

    pad = ;
    n = src_len;
    src = (unsigned char *)malloc(n);

    for (i=; i<n; i++)
        src[i]=base64code[i];

    while (n>&&src[n-]=='=')
    {
        src[n-] = ;
        pad++;
        n--;
    }

    for(i=; i<n; i++)
    {
        p = (unsigned char *)strchr((const char *)base64, (int)src[i]);
        if (!p)
        {
            free(src);
            return -;
        }

        src[i] = p-(unsigned char *)base64;
    }

    for (i=,j=; i<n; i+=,j+=)
    {
        buf[j] = (src[i]<<) + ((src[i+]&)>>);
        buf[j+] = ((src[i+]&)<<) + ((src[i+]&)>>);
        buf[j+] = ((src[i+]&)<<) + src[i+];
    }

    j -= pad;
    buf[j] = ;
    free(src);

    return j;
}

int main()
{
    // 測試Base64編碼
    {
        char buff[] = "";
        char *str = "0123456789abcdef";

        memset(buff, , ); // clear
        int base64len = Base64Encode(buff, str, strlen(str));
        buff[base64len] = ;

        printf("%s\n", buff);
    }

    // 測試Base64解碼
    {
        char buff[] = "";
        char *str = "MDEyMzQ1Njc4OWFiY2RlZg==";

        memset(buff, , );
        int base64len = Base64Decode(buff, str, strlen(str));

        printf("%s\n", buff);
    }

    return ;
}
           

編譯 && 執行:

[jiang@localhost jiang]$ gcc -o Base64 Base64.c 
[jiang@localhost jiang]$ ./Base64 
MDEyMzQ1Njc4OWFiY2RlZg==
89abcdef
           

需要注意:

為啥在編碼時需要指明待編碼資料長度?

原因是待編碼資料不一定可讀,是二進制資料,即,可能存在0x00,很顯然不可以用strlen,那我在編碼函數中如何知道其待編碼資料長度呢?不知道!是以,調用編碼函數時需要顯示地指明待編碼資料長度。

引用:

1.http://base64.xpcha.com/

繼續閱讀