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

http://base64.xpcha.com/

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 == 0)
        src_len = strlen(src);

    buflen=n=src_len;

    for (i=0,j=0; i<=buflen-3; i+=3,j+=4)
    {
        base64code[j] = (src[i]&0xFC)>>2;
        base64code[j+1] = ((src[i]&0x03)<<4) + ((src[i+1]&0xF0)>>4);
        base64code[j+2] = ((src[i+1]&0x0F)<<2) + ((src[i+2]&0xC0)>>6);
        base64code[j+3] = src[i+2]&0x3F;
    }

    if (n%3==1)
    {
        base64code[j] = (src[i]&0xFC)>>2;
        base64code[j+1] = ((src[i]&0x03)<<4);
        base64code[j+2] = 64;
        base64code[j+3] = 64;
        j += 4;
    }
    else if (n%3==2)
    {
        base64code[j] = (src[i]&0xFC)>>2;
        base64code[j+1] = ((src[i]&0x03)<<4) + ((src[i+1]&0xF0)>>4);
        base64code[j+2] = ((src[i+1]&0x0F)<<2);
        base64code[j+3] = 64;
        j += 4;
    }

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

    base64code[j] = 0;

    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 == 0)
        src_len = strlen(base64code);

    if (src_len%4 != 0)
        return -1;

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

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

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

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

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

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

    for (i=0,j=0; i<n; i+=4,j+=3)
    {
        buf[j] = (src[i]<<2) + ((src[i+1]&0x30)>>4);
        buf[j+1] = ((src[i+1]&0x0F)<<4) + ((src[i+2]&0x3C)>>2);
        buf[j+2] = ((src[i+2]&0x03)<<6) + src[i+3];
    }

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

    return j;
}

int main()
{
    // 测试Base64编码
    {
        char buff[1024] = "";
        char *str = "0123456789abcdef";

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

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

    // 测试Base64解码
    {
        char buff[1024] = "";
        char *str = "MDEyMzQ1Njc4OWFiY2RlZg==";

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

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

    return 0;
}

编译 && 执行:

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

需要注意:

为啥在编码时需要指明待编码数据长度?

原因是待编码数据不一定可读,是二进制数据,即,可能存在0x00,很显然不可以用strlen,那我在编码函数中如何知道其待编码数据长度呢?不知道!所以,调用编码函数时需要显示地指明待编码数据长度。

引用:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值