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

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的文本文档中,当另存为时,在编码类型中,有如下几个选择。

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

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的各个位的数值。转化结果如下:

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

执行结果如下:

完成这篇博客,从基础知识的准备到base64原理的了解,再到最终代码的实现和调试,花费了一天的时间,具体原理不难,调试过程中,遇到了很多问题,深知,网上得来终觉浅,绝知此事要躬行。别人给出的代码,自己如果不敲一片,字字斟酌,如果只是匆匆扫过,是怎么样也不会体会别人的思路和方法。现在网络资源异常丰富,我们在别人的基础上,进行自己的改进和优化,博采众长,提升自己。

对代码来说,光说不练假把式,虽说是今天实现的只是一个小小的功能,把小功能步步都想清楚,各种情况都处理好,每天深入了解学习一个知识点,能够解决一个问题,那这一天就很有价值,没有白白度过。

Base64编码原理与应用的更多相关文章

  1. BASE64编码原理分析脚本实现及逆向案例

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理你又了解多少?今天小编带大家了解一下Base64编码原理分析脚本实现及逆向案例的相关内容.   01编码由来 数 ...

  2. Atitit.Base64编码原理与实现设计

    Atitit.Base64编码原理与实现设计 1. Base64编码1 1.1. 为什么要用自己的base64编码方案1 2. Base64编码由来1 3. Base64编码原理1 3.1. 具体来说 ...

  3. Base64编码原理及应用

    最近在做一个H5上传图片并压缩的项目,其过程主要是先将图片上传通过readAsDataURL获取上传图片base64编码,然后根据高宽比将图片画到canvas上实现压缩,在通过toDataURL获取压 ...

  4. Base64 编码原理

    什么是 Base64 编码 Base64 编码是最常见的编码方式,基于 64 个可打印字符来表示任意二进制数据的方法,是从二进制转换到可见字符的过程. 使用场景 数据加密或签名通过 Base64 转换 ...

  5. Base64编码原理分析

    Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,在了解Base64编码之前,先了解几个基本概念:位.字节. 位:"位(bit)"是计算机中最小的数据单位.每一位 ...

  6. 一篇文章彻底弄懂Base64编码原理

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...

  7. 一篇文章彻底弄懂Base64编码原理(转载)

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...

  8. 知识扩展——(转)一篇文章彻底弄懂Base64编码原理

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. 一.Base64的由来 目前Base64已经成 ...

  9. 一篇文章彻底搞懂base64编码原理

    开始 在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇文章带领大家了解一下Base64的底层实现. base64是什么东东呢? Base64 ...

随机推荐

  1. Spring生态

    1.简洁有力,干掉了j2ee容器层特别是ejb,spring在rod Johnson十几年前一个人单挑j2ee体系开始,到十年前开始大行其道至今,基本上是java开发领域的事实标准.从此大部分开发者去 ...

  2. [MEAN Stack] First API -- 5. Using $resource to setup REST app

    Front-end changes: app.js: Uinsg $resource /** * Created by Answer1215 on 12/9/2014. */ 'use strict' ...

  3. iOS开发——总结篇&常用开发总结

    一.通知1.监听通知 - (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(i ...

  4. SAP SOAMANAGER 配置WEBSERVICE 提示:Service cannot be reached解决方法

    TM中有些服务没有被激活,以UI界面个性化设置化设置为例: 如果服务没有被激活,打开界面就会显示: 这时候右键点击属性,获取服务ID: 通过事务代码SICF,输入服务ID:wd_analyze_con ...

  5. 关于flash player debugger 无法弹窗报错的解决办法

    第一个是IE的插件, Download the Windows Flash Player 10.2 ActiveX control content debugger (for IE) (EXE, 2. ...

  6. MapReduce原理讲解

    简介 本文主要介绍MapReduce V2的基本原理, 也是笔者在学习MR的学习笔记整理. 本文首先大概介绍下MRV2的客户端跟服务器交互的两个协议, 然后着重介绍MRV2的核心模块MRAppMast ...

  7. Mac电脑没有声音,苹果电脑没有声音怎么办

      对于使用 Windows 系统电脑的小伙伴来说,可能有很多人会遇到电脑没有声音的问题.苹果 Mac 电脑也会出现没有声音的问题,不过相对较少.这里以我遇到的一个没有声音的问题为例,简单介绍处解决的 ...

  8. Android入门:一、Android Studio 2.1安装及初始化配置

    以前研究过eclipse +ADT开发android app,没深入再加上工作也用不上就扔在那,现在需要做APP开发,发现eclipse +ADT也不再更新了,google推出了功能强大的Androi ...

  9. Oracle中exists与in的区别

    有两个简单例子,以说明 "exists"和"in"的效率问题 1) select * from T1 where exists(select 1 from T2 ...

  10. redis中5种数据结构的使用

    一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 ...