Base64编码原理与应用
本文内容转自网络,如需详细内容,请参考相关网址。
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编码原理与应用的更多相关文章
- BASE64编码原理分析脚本实现及逆向案例
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理你又了解多少?今天小编带大家了解一下Base64编码原理分析脚本实现及逆向案例的相关内容. 01编码由来 数 ...
- Atitit.Base64编码原理与实现设计
Atitit.Base64编码原理与实现设计 1. Base64编码1 1.1. 为什么要用自己的base64编码方案1 2. Base64编码由来1 3. Base64编码原理1 3.1. 具体来说 ...
- Base64编码原理及应用
最近在做一个H5上传图片并压缩的项目,其过程主要是先将图片上传通过readAsDataURL获取上传图片base64编码,然后根据高宽比将图片画到canvas上实现压缩,在通过toDataURL获取压 ...
- Base64 编码原理
什么是 Base64 编码 Base64 编码是最常见的编码方式,基于 64 个可打印字符来表示任意二进制数据的方法,是从二进制转换到可见字符的过程. 使用场景 数据加密或签名通过 Base64 转换 ...
- Base64编码原理分析
Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,在了解Base64编码之前,先了解几个基本概念:位.字节. 位:"位(bit)"是计算机中最小的数据单位.每一位 ...
- 一篇文章彻底弄懂Base64编码原理
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...
- 一篇文章彻底弄懂Base64编码原理(转载)
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...
- 知识扩展——(转)一篇文章彻底弄懂Base64编码原理
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. 一.Base64的由来 目前Base64已经成 ...
- 一篇文章彻底搞懂base64编码原理
开始 在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇文章带领大家了解一下Base64的底层实现. base64是什么东东呢? Base64 ...
随机推荐
- SQL SERVER NVARCHAR字段INSERT 中文乱码问题解决
INSERT INTO IPatient ( UID, PatientNo, PatientName, PatientGender, Birthday, BloodType, Country, Nat ...
- C# Process 类的思考
在这里,我先给自己留个印象 下面我们用C#实现一个调用Dos命令的小程序,让大家对系统进程能有个直观的了解.要使用Process类,首先要引入System.Diagnostic命名空间,然后定义一个新 ...
- PhotoShop中画圆角矩形最简单方法(图文并茂)!
PhotoShop中画圆角矩形最简单方法(图文并茂)! 1. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZHhubjUyMA==/font/5a6L5L ...
- Android无法生成R文件的终极解决办法
R文件如果在clean项目(Project—>Clean)和 Fix Project Properties(如下图): 如果在第一步无法解决的的时候,那可能原因就是资源文件调用的错误,比如资 ...
- 支持向量机(SVM)非线性数据切割
支持向量机(SVM)非线性数据切割 1.目标 本指导中你将学到: l 当不可能线性切割训练数据时,如何定义SVM最优化问题. l 在这样的问题上.如何配置CvSVMParams中的參数满足你的SV ...
- iOS开发——UI_swift篇&TableView实现页眉和页脚
TableView实现页眉和页脚 在UItableView中header和footer是很常见的,而且他能让你实现很复杂的功能,我们见过最多的就是下拉刷新和上啦加载更多,当然你还可以在上面添加一个 ...
- _vsnprintf 用法
_vsnprintf,C语言库函数之一,属于可变参数.用于向字符串中打印数据.数据格式用户自定义. 头文件: #include <stdarg.h> 函数声明: int _vsnprint ...
- Python integer objects implementation
http://www.laurentluce.com/posts/python-integer-objects-implementation/ Python integer objects imple ...
- JavaWeb中的简单分页
这次主要是讲解一下通过登录后对得到的数据进行分页,首先我们新建一个登录页面login.jsp,因为我们主要学习一下分页,所以登录验证的部分不再阐述,主要代码如下: <form action=&q ...
- 使用NumberPicker定制自己喜欢的Date&TimePicker
1.NumberPicker简介: NumberPicker是Android3.0之后引入的一个控件,主要功能是用于选择一组预定义好数字的控件. 该控件主要需要指导一个用于监听当前value变化的li ...