天天看点

编解码: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/

继续阅读