Base64编码原理与应用

转载 2017年03月01日 10:10:06

先贴一个自己实现的代码

#ifndef _BASE_64_H
#define _BASE_64_H

char* base64_encode(const char* data, int data_len); 
char* base64_decode(const char* data, int data_len); 

#endif

#include "base64.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "log.h"
const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 
//char* base64_encode(const char* data, int data_len); 
//char *base64_decode(const char* data, int data_len); 
static char find_pos(char ch); 
/*
int main(int argc, char* argv[]) 
{ 
    char *t = "那个abcd你好吗,哈哈,ANMOL"; 
    int i = 0; 
    int j = strlen(t); 
    char *enc = base64_encode(t, j); 
    int len = strlen(enc); 
    char *dec = base64_decode(enc, len); 
    printf("\noriginal: %s\n", t); 
    printf("\nencoded : %s\n", enc); 
    printf("\ndecoded : %s\n", dec); 
    free(enc); 
    free(dec); 
    return 0; 
}
*/

/* */ 
char *base64_encode(const char* data, int data_len) 
{ 
    //int data_len = strlen(data); 
    int prepare = 0; 
    int ret_len; 
    int temp = 0; 
    char *ret = NULL; 
    char *f = NULL; 
    int tmp = 0; 
    char changed[4]; 
    int i = 0; 
    ret_len = data_len / 3; 
    temp = data_len % 3; 
    if (temp > 0) 
    { 
        ret_len += 1; 
    } 
    ret_len = ret_len*4 + 1; 
    ret = (char *)malloc(ret_len); 
      
    if ( ret == NULL) 
    { 
        printf("No enough memory.\n");
        CTS_LOG_FATAL("base64_encode:No enough memory....\n");
        
        exit(0); 
    } 
    memset(ret, 0, ret_len); 
    f = ret;
    while (tmp < data_len) 
    { 
        temp = 0; 
        prepare = 0; 
        memset(changed, '\0', 4);
        
        while (temp < 3) 
        { 
            //printf("tmp = %d\n", tmp); 
            if (tmp >= data_len) 
            { 
                break; 
            }
            
            prepare = ((prepare << 8) | (data[tmp] & 0xFF));
            
            tmp++; 
            temp++;
        }
        
        prepare = (prepare<<((3-temp)*8)); 
        //printf("before for : temp = %d, prepare = %d\n", temp, prepare);
        for (i = 0; i < 4 ;i++ ) 
        { 
            if (temp < i) 
            { 
                changed[i] = 0x40; 
            } 
            else 
            { 
                changed[i] = (prepare>>((3-i)*6)) & 0x3F; 
            } 
            *f = base[changed[i]]; 
            //printf("%.2X", changed[i]); 
            f++; 
        }
    }
    
    *f = '\0'; 
    
    return ret; 
      
} 
/* */ 
static char find_pos(char ch)   
{ 
    char *ptr = (char*)strrchr(base, ch);//the last position (the only) in base[] 
    return (ptr - base); 
} 
/* */ 
char *base64_decode(const char *data, int data_len) 
{ 
    int ret_len = (data_len / 4) * 3; 
    int equal_count = 0; 
    char *ret = NULL; 
    char *f = NULL; 
    int tmp = 0; 
    int temp = 0; 
    char need[3]; 
    int prepare = 0; 
    int i = 0; 
    if (*(data + data_len - 1) == '=') 
    { 
        equal_count += 1; 
    } 
    if (*(data + data_len - 2) == '=') 
    { 
        equal_count += 1; 
    } 
    if (*(data + data_len - 3) == '=') 
    {//seems impossible 
        equal_count += 1; 
    } 
    switch (equal_count) 
    { 
    case 0: 
        ret_len += 4;//3 + 1 [1 for NULL] 
        break; 
    case 1: 
        ret_len += 4;//Ceil((6*3)/8)+1 
        break; 
    case 2: 
        ret_len += 3;//Ceil((6*2)/8)+1 
        break; 
    case 3: 
        ret_len += 2;//Ceil((6*1)/8)+1 
        break; 
    } 
    ret = (char *)malloc(ret_len); 
    if (ret == NULL) 
    { 
        printf("No enough memory.\n"); 
        exit(0); 
    } 
    memset(ret, 0, ret_len); 
    f = ret; 
    while (tmp < (data_len - equal_count)) 
    { 
        temp = 0; 
        prepare = 0; 
        memset(need, 0, 4); 
        while (temp < 4) 
        { 
            if (tmp >= (data_len - equal_count)) 
            { 
                break; 
            } 
            prepare = (prepare << 6) | (find_pos(data[tmp])); 
            temp++; 
            tmp++; 
        } 
        prepare = prepare << ((4-temp) * 6); 
        for (i=0; i<3 ;i++ ) 
        { 
            if (i == temp) 
            { 
                break; 
            } 
            *f = (char)((prepare>>((2-i)*8)) & 0xFF); 
            f++; 
        } 
    } 
    *f = '\0'; 
    return ret; 
}




/////////////////////////////////////////////下边是拷贝网上资料////////////////////////////////////////////////////////////////////////////////////


本文内容转自网络,如需详细内容,请参考相关网址。

http://my.oschina.net/goal/blog/201032

代码参考:http://blog.csdn.net/prsniper/article/details/7097643

 

 

Base64,它用作把任意序列的8位字节描述为一种不易被人直接识别的形式,常用作开发中用于传递参数、浏览器的img标签通过base64字符串来渲染图片以及电子邮件的正文编码等等。

在计算机中显示的字符,比如英文字母、数字以及英文标点符号就是用一个字节来存储,通常称为ASCII码。而简体中文、繁体中文、日文以及韩文等都是用多字节来存储的,通常称之为多字节字符。因为Base编码的输入是字符串的编码,不同编码的字符串的Base64结果是不同的。所以,先来介绍基本的字符编码知识。

字符编码基本知识

最开始的计算机,只支持ASCII码,不支持中文等其他字符,一个字符采用一个字节(8位)表示,只使用低7位,最高位固定为0,因此总共有128个ASCII码(取值范围:0~127)。

为了支持更多地区的语言,各大组织机构和IT厂商开始推广自己的编码方案,以弥补ASCII编码的不足,如GB2312编码、GBK编码和Big5编码,这些编码只是针对局部地区的文字,往下兼容ASCII码,没办法表达所有的语言,这些不同的编码之间没有任何联系,他们之间的转换需要通过查表来实现。

为了提高计算机的信息处理和交换能力,使得各国文字都能在计算机中处理。国际ISO组织制定了通用多字节编码字符集(ISO 10646),简称UCS,这一标准为世界各种主要语言的字符以及附加符号,编制统一的内码。Unicode是Unicode学术学会机构制定的编码系统,从内容上来看,和UCS是同步一致的。

ANSI不代表具体的编码,而是代指本地编码,比如在简体中文版的Windows上面,它代表GB2312编码,在繁体中文版上面,它代表Big5编码,在日文操作系统上面,代表JIS编码。所以,当用户新建并且保存文件类型为ANSI编码,那么那会根据本地系统的编码来确定具体的编码。

Unicode编码

Unicode编码表和字符表是一一映射的,比如汉字”回“,其Unicode编码为56DE,通过56DE就能在Unicode表中找到汉字”回“。Unicode本身定义了每个字符的数值,是字符和自然数的映射关系,而UTF-8、UTF-16则定义了如何在字节流中断字。现在最为常用的Unicode编码为UTF-8和UTF-16.下图为常用的UTF-8的编码形式。在线汉字编码查询点此进入。

从图中可以看出,UTF-8为变长的编码方式(1~6个字节),向下兼容ASCII编码,通常将UTF-8看做单字节或三字节的实现,其他情况非常罕见。每个字节的开始很有规律,方便处理。

UTF-16

UTF-16编码是最直接的Unicode的实现方式,它采用固定两个字节来存储,因为是多字节,所以有小端存储和大端存储两种方式。UTF-16编码是Windows上默认的Unocode编码方式,两个字节小端存储。

在Windows的文本文档中,当另存为时,在编码类型中,有如下几个选择。

image 就同样一个汉字”回“字,其不同的编码内容如下:

image

unicode编码结果上面,前面的两个字节FF FE是文件头,代表这是一个UTF16编码的文件,DE 56是”回“字的UTF16编码十六进制表示,低位字节为DE,高位字节为56,组合在一起,就是0x56DE。

有了上述的知识积累,就可以很方便的在UTF8和UTF16之间相互转换了,还是以”回“字为例子,UTF16编码值为  0x56DE,它在0x0000_0800~0x0000_FFFF之间,对于的UTF8字节为三字节。所以,这个转换就是将2字节的UTF16转换为3字节的UTF8编码。注意到UTF-8表中的转换图中的x部分,就是对应0x56DE的各个位的数值。转化结果如下:image

UTF8转换为UTF16就是上面的逆过程,知道了转换规则,很容易实现其代码。

中国大陆使用的中文标准为GB2312,一共收录了7445个常用简体汉字和中文符号。

Big5是台湾使用的编码标准,大约编码了8千多个繁体汉字。

HKSCS是香港地区使用的编码标准,但和Big5有所不同。

上述这几套中文编码互不兼容,妨碍软件开发,国际上针对此情况,制定了针对中文的统一字符集GBK和GB18030,其中,GBK已经在Windows、Linux等多种操作系统上实现。GBK1.0收录了21886个符号,分为汉字区和图形符号区,2000年的GB18030是取代GBK1.0,成为正式国家标准,该标准收录了27484个汉字,还收录了藏文、蒙文、维吾尔文等主要少数民族汉字。一般设备上,只需要支持GB2312就足够了。

从ASCII、GB2312、GBK到GB18030,这些编码是向下兼容的,其中,区分中文编码的方法是高字节的最高位不为0.

Unicode只与ASCII兼容,与GB系列码不兼容。例如,“汉”字的Unicode码为6C49,GB码为BABA。

 

上面讲述了最基本的ASCII、UTF-8、UTF-16等编码的基础知识和对于的转化规则,下面开始介绍本文重点内容Base64编码。

 

Base64编码

Base64用在必须用可打印字符表示二进制内容的场合,将任意字节转为可读字符的编码,这种编码,不是为安全,因为它是可逆的,而是为了显示。比如需要在xml文档中包含一段音频或者数字签名,URL传递参数,电子邮件的传输编码,可打印字符包括大小写字母(A-Z,a-z),数字(0-9),加号(“+”),正斜杠(“/”),外加补全符号(“=”)

Base64编码要求把3个8位字节(3*8=24位)编码成4个6位的字节(4*6=24位),之后在每个6位字节前面,补充两个0,形成4个8位字节的形式(取值范围在0~63),由于2^6次方等于64,所以每6个位组成一个单元,对于某个可打印的字符,当原始数据不是3的整数倍时,

当最后剩下一个输入字节时,在编码后面添加两个”=”

当最后剩下两个输入字节时,在编码后面添加一个“=”

当数据可以被3整除,就不需要添加数据。

下图为Base64转码表:

上述为标准的Base64编码,标准的Base64编码不适合直接放在URL里面传输,因为URL编码器会将”/“和”+”字符转变为形如”%XX”的形式,而这些”%”号在存入数据库时,还需要转换,因为ANSI SQL中”%“是通配符。

人们为了解决此问题,提出了用于URL的改进Base64编码,它不在末尾填充”=”,并且将标准Base64中的”+”和”/”分别改成了”-”和“_”,这样就免去URL的编解码和数据存储时的格式转换,长度保持不变,统一了数据库、表单等处理对象的格式。

编码过程,简单下来可以总结为:

1. 先将输入的字节数凑成3的整数倍N,然后申请N*4/3+1这么多的空间内存空间

2. 按照3个字节为一组,转换为4个字节的输出原则转换,特别要注意申请空间的最后一位的处理

3. 解密时,有取值判断和查表两种方法,推荐使用查表法。

4. 在字符串输出的最后,需要加上结束标志符’\0’

 

下面给出C语言的Base64代码,在网友给出的代码基础上,经过自己认真测试,现在贡献出来,希望能够帮助到其他人。我在这里面,对结束符的处理是映射为0xFF,个人觉的,只要知道这个标志出现了,做适当的处理就可以了。

 

复制代码
/**************************************************************************************************
  Filename:       base64.c
  Revised:        2014-12-09 15.18

  Description: this file use to descript the Base64 encode and decode
  Author:          huhao
  Email:           huhao0126@163.com
***************************************************************************************************/

#include "stdlib.h"
#include "base64.h"


static const char BASE_CODE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const unsigned char base64_dec_map[128] =
{
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,   //0 ~ 9
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,   //10~19
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,   //20~29
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,   //30~39
    127, 127, 127,  62, 127, 127, 127,  63,  52,  53,   //40~49
    54,  55,  56,  57,  58,  59,  60,  61, 127, 127,    //50~59
    127, 0xFF, 0, 0, 0,   0,   1,   2,   3,   4,        //60~69
    5,   6,   7,   8,   9,  10,  11,  12,  13,  14,     //70~79
    15,  16,  17,  18,  19,  20,  21,  22,  23,  24,    //80~89
    25, 127, 127, 127, 127, 127, 127,  26,  27,  28,    //90~99
    29,  30,  31,  32,  33,  34,  35,  36,  37,  38,    //100~109
    39,  40,  41,  42,  43,  44,  45,  46,  47,  48,    //110~119
    49,  50,  51, 127, 127, 127, 127, 127               //120~127
};

/**************************************************************************************************
 * @fn          base64_encode
 *
 * @brief       This function encode the bindata into base64 format.
 *
 * input parameters
 *
 * @param       bindata   - the input bindata
 * @param       output    - the output base64 data.
 * @param       slen      - the  length of input data.
 *
 * output parameters
 *
 * None.
 *
 * @return the length of encoder output
 **************************************************************************************************
 */
int Base64Encode(const unsigned char* bindata, unsigned char* output,int slen)
{
    int vlen = 0;
    unsigned char* temp_data;

    temp_data = bindata;

    while(slen > 0 )
    {
        *output++ = BASE_CODE[  (temp_data[0]>>2) & 0x3F ];
        if(slen > 2 ) //长度大于三个字符 处理生成4个字符
        {
            *output++ = BASE_CODE[  ( ( temp_data[0] & 0x03 )<<4) | ( temp_data[1] >>4) ];
            *output++ = BASE_CODE[  ( ( temp_data[1] & 0x0F )<<2) | ( temp_data[2] >>6) ];
            *output++ = BASE_CODE[    ( temp_data[2] & 0x3F )];
        }else if( slen  == 2)  //恰好为两个字符
        {
            *output++ = BASE_CODE[  ( ( temp_data[0] & 0x03 )<<4) | ( temp_data[1] >>4) ];
            *output++ = BASE_CODE[  ( ( temp_data[1] & 0x0F )<<2)];
            *output++ = '=';
        }else if( slen == 1)   //恰好为一个字符
        {
            *output++ = BASE_CODE[ (temp_data[0]&0x03) << 4];
            *output++ = '=';
            *output++ = '=';
        }

        temp_data += 3;
        slen    -= 3;
        vlen    += 4;
    }
    *output = '\0';     //this is very improtant
    return vlen;
}

/**************************************************************************************************
 * @fn          GetCharIndex
 *
 * @brief       This function get the mapping value.
 *
 * input parameters
 *
 * @param       c       - Base64 code
 *
 * output parameters
 *              none
 *  it has two ways to map from ciphertext to plaintext,one is lookup-table,and the other is value judgements.
 * @return  original value
**************************************************************************************************
 */
unsigned char GetCharIndex(unsigned char c)
{
    #if 1
    if(  ( c >= 'A' )  && ( c <= 'Z'))
    {
        return c - 'A';
    }else if( (c >= 'a') && (c <= 'z' ))
    {
        return c-'a'+26;
    }else if( (c >= '0') && ( c <= '9'))
    {
        return c - '0' + 52;
    }
    else if( c == '+')
    {
        return 62;
    }else if( c == '/')
    {
        return 63;
    }else if(c == '=')
    {
        return 0xFF;
    }
    #else
    return base64_dec_map[c];
    #endif
    return 0;
}

/**************************************************************************************************
 * @fn          BaseDecode
 *
 * @brief       This function decode the bindata into base64 format.
 *
 * input parameters
 *
 * @param       input    - the encoded input data
 * @param       output   - the decode  output data
 * @param       sLen     - the length of encoded input data.
 *
 * output parameters
 *              none
 *
 * @return      vlen     - the length of decode data
 **************************************************************************************************
 */
int Base64Decode(const unsigned char* input,unsigned char *output,int sLen)
{
    static unsigned char lpCode[4] = {0};
    unsigned char* data_temp = input;
    int vlen = 0;

    //Base64 length must be a multiple of 4 including '='
    if( sLen % 4 )
    {
        return -1;
    }

    while(sLen > 0 )
    {
        lpCode[0] = GetCharIndex(data_temp[0]);
        lpCode[1] = GetCharIndex(data_temp[1]);
        lpCode[2] = GetCharIndex(data_temp[2]);
        lpCode[3] = GetCharIndex(data_temp[3]);

        if(  lpCode[3] == 0xFF )
        {
            if( lpCode[2] == 0xFF )   // if there has two '=' at the end
            {
                *output++ = (lpCode[0] << 2) | (lpCode[1] >>4);
                vlen +=1;
                break;
            }else                   // if there has one '=' at the end
            {
                *output++ = (lpCode[0] << 2) | (lpCode[1] >>4);
                *output++ = (lpCode[1] << 4) | (lpCode[2] >>2);
                vlen +=2;
                break;
            }
        }else
        {
            *output++ = (lpCode[0] << 2) | (lpCode[1] >>4);
            *output++ = (lpCode[1] << 4) | (lpCode[2] >>2);
            *output++ = (lpCode[2] << 6) | (lpCode[3]);

            data_temp+=4;
            sLen -=4;
            vlen +=3;
        }
    }

    *output = '\0'; //this is very improtant
    return vlen;
}


/*
main test function
*/
int test_base64()
{
    unsigned char* output_buffer = NULL;
    char input_str[100]={0};

    int allocate_len;
    int input_len;
    int output_len;

    printf("Please input string : \n ");
    scanf("%s",input_str);

    input_len = strlen(input_str);
    printf("the length of input is %d \n",strlen(input_str));

    //补齐字节数,使得输出缓存为4的倍数,这样来考虑,先把输入长度补齐到3的倍数,然后将其乘以4再除以3
    allocate_len =  ( input_len % 3 ) ? (  input_len + 3 - input_len%3 ) :( input_len);
    allocate_len =  ( allocate_len * 4 )/3;

    output_buffer = (unsigned char*)malloc(allocate_len+1);     //加1 很重要,用于结束编解码的输出字符串
    if(NULL == output_buffer)
    {
        printf("allocate memory fail! \n");
        return -1;
    }else
    {
        printf("success allocate %d bytes. \n",allocate_len);
        memset(output_buffer,0,allocate_len);
    }

    output_len = Base64Encode(input_str,output_buffer,input_len);

    printf("Base64ENcode(\" %s \" ,encoding output length is %d \")  \n encode content is %s \" \n\n",input_str,output_len,output_buffer);

    memset(input_str,0,sizeof(input_str));
    output_len = Base64Decode(output_buffer,input_str,output_len);

    printf("Base64Decode(\" %s  \" ,decoding output length is %d \")  \n decode content is %s   \n",output_buffer,output_len,input_str);

    free(output_buffer);        //release the allocated memory
    output_buffer = NULL;
}
复制代码

执行结果如下:

image

相关文章推荐

Windows与Linux下Base64编码问题

Java中,String有很多个构造方法,其中有三个如下图所示: 人们习惯性喜欢使用没有字符集的构造方法,因为简单!而我也正是犯了同样的错误!...

Linux base64 --用base64编解码

用途说明 base64是一种常用的简单的编解码方式。以下内容摘自维基百科。   维基百科 Base64 写道 在MIME格式的电子邮件中,base64可以用来将binary的字节序列数据编码成...

Base64编码解码原理及实现

  • 2011年11月01日 21:15
  • 140KB
  • 下载

关于DataURI base64编码的原理及实现

我们的图片大部分都是可以转换成base64编码的data:image。 这个在将canvas保存为img的时候尤其有用。虽然除ie外,大部分现代浏览器都已经支持原生的基于base64的encode和d...
  • sqzhao
  • sqzhao
  • 2015年09月25日 17:49
  • 673

Base64编码原理与实现

来自 李宁的极客世界     Base64编码的原理是按bit将每6个bit转换成Base64编码表中的相应字符。下面是Base64的编码表: 0 A 17 R 34 i 51 z  1 B...
  • ysmz4
  • ysmz4
  • 2013年11月04日 17:24
  • 572

关于base64编码的原理及实现

关于base64编码的原理及实现 我们的图片大部分都是可以转换成base64编码的data:image。 这个在将canvas保存为img的时候尤其有用。虽然除ie外,大部分现代浏览器都已经支持原...

Base64编码解码原理详解

Base64编码解码原理详解 1. Base64字符的组成部分 Base64所用字符: 0,1,2 ….9 A,B,C,D…Z a,b,c,d…z + / 对应ASCII: 48,4...

从原理上搞定Base64编码

原文地址: 从原理上搞定编码(四)-- Base64编码 开发者对Base64编码肯定很熟悉,是否对它有很清晰的认识就不一定了。实际上Base64已经简单到不能再简单了,如果对...

Base64编码解码原理

一. Base64编码由来  为什么会有Base64编码呢?因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就不能通过邮件传送。这样用途就受到了很大...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Base64编码原理与应用
举报原因:
原因补充:

(最多只允许输入30个字)