base64简介
base64网络上已经有很多人有讲述,下面摘录wiki上的简述:
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。三个字节有24个比特,对应于4个Base64单元,即3个字节可表示4个可打印字符。它可用来作为电子邮件的传输编码。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。
在MIME格式的电子邮件中,base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。
完整的base64定义可见RFC 1421和RFC 2045。编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲器中剩下的bit用0补足。然后,每次取出6(因为26=64)个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。当原数据长度不是3的整数倍时, 如果最后剩下一个输入数据,在编码结果后加2个“=”;如果最后剩下两个输入数据,编码结果后加1个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证数据还原的正确性。
完整的base64说明可以参考wiki_base64
本文中主要陈述的是base64编码的一种C++代码实现。
base64_operation.h
先列出实现的API的声明头文件:
#ifndef _BASE64_OPERATION_H_
#define _BASE64_OPERATION_H_
#include <iostream>
#include <stdio.h>
typedef enum
{
E_BASH64_OK = 0,
E_BASH64_ERROR_PARAM,
E_BASH64_FILE_ERROR,
E_BASH64_BUFFER_OVERFLOW,
E_BASH64_ERROR_USUAL
}BASE64_RESULT;
class Base64_Operation
{
public:
/**
* [base64_GetInstance Get the object pointer]
* @return [the object pointer]
*/
static Base64_Operation* base64_GetInstance(void);
Base64_Operation();
~Base64_Operation();
/**
* [base64_file_encode Ecode a file to base64 format file]
* @param pInFile [The input file pointer from fopen(), must be opened with "rb"]
* @param pOutFile [The output file pointer from fopen(), must be opened with "wb+"]
* @return [The error code]
*/
BASE64_RESULT base64_file_encode(FILE* pInFile, FILE* pOutFile);
/**
* [base64_file_decode decode a file from base64 format file]
* @param pInFile [The input file pointer from fopen(), must be opened with "rb"]
* @param pOutFile [The output file pointer from fopen(), must be opened with "wb+"]
* @return [The error code]
*/
BASE64_RESULT base64_file_decode(FILE* pInFile, FILE* pOutFile);
/**
* [base64_hex_encode Encode a buffer data to base64 format]
* @param pInBuffer [The normal data buffer]
* @param InBufferLen [The normal data length]
* @param pOutBuffer [The output buffer]
* @param pOutBufferLen [The output buffer of base64 length]
* @return [The error code]
*/
BASE64_RESULT base64_hex_encode(char* pInBuffer, unsigned int InBufferLen, char *pOutBuffer,unsigned int *pOutBufferLen);
/**
* [base64_hex_decode decode a buffer data from base64 format]
* @param pInBuffer [The base64 format buffer]
* @param InBufferLen [The base64 buffer length]
* @param pOutBuffer [The output buffer pointer]
* @param pOutBufferLen [The output buffer length]
* @return [The error code]
*/
BASE64_RESULT base64_hex_decode(char* pInBuffer, unsigned int InBufferLen, char *pOutBuffer,unsigned int *pOutBufferLen);
protected:
private:
static Base64_Operation* Instance;
static void _initDecTable(void);
static unsigned int _singleGroupDecode(char* inBuffer, char* outBuffer);
static void _singleGroupEncode(char* inBuffer, char* outBuffer);
};
#endif
主要有实现基于用户buffer的base64的编码与解码(支持小于unsigned int所能表示的最大长度);实现了文件的base64的编码与解码。另外base64完整规范有要求每隔76bytes需要有换行,本实现没有考虑这种要求,鄙人认为用户自己在编码的base64上按自己的需求去增加换行符应该会更好;
base64_operation.cpp
#include "base64_operation.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using std::cout;
using std::cin;
using std::endl;
using std::hex;
#define DECTABLE_LEN 123
#define EQUAL_SIGN_MAGIC 0xFF
#define FILE_BUFFER_LEN 0x200000
#define FILE_OUTTMP_LEN 0x300000
#define OPEN_DBG 0
Base64_Operation* Base64_Operation::Instance = NULL;
static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static unsigned char charDecTable[DECTABLE_LEN+1];
void Base64_Operation::_initDecTable()
{
int index = 0;
memset(charDecTable, 0x0, DECTABLE_LEN);
for(index = 'A'; index <= 'Z';index++)
{
charDecTable[index] = index - 'A';
}
for(index = 'a'; index <= 'z';index++)
{
charDecTable[index] = index - 'a' + 26;
}
for(index = '0'; index <= '9';index++)
{
charDecTable[index] = index - '0' + 52;
}
charDecTable['+'] = 62;
charDecTable['/'] = 63;
charDecTable['='] = EQUAL_SIGN_MAGIC;
}
void Base64_Operation::_singleGroupEncode(char* inBuffer, char* outBuffer)
{
unsigned int order;
order = (inBuffer[0]&0xFC)>>2;
outBuffer[0] = base64Char[order];
#if OPEN_DBG
cout<<order<<endl;
cout<<outBuffer[0]<<endl;
#endif
order = ((inBuffer[0]&0x3)<<4)|((inBuffer[1]&0xF0)>>4);
outBuffer[1] = base64Char[order];
#if OPEN_DBG
cout<<order<<endl;
cout<<outBuffer[1]<<endl;
#endif
order = ((inBuffer[1]&0xF)<<2)|((inBuffer[2]&0xC0)>>6);
outBuffer[2] = base64Char[order];
#if OPEN_DBG
cout<<order<<endl;
cout<<outBuffer[2]<<endl;
#endif
order = inBuffer[2]&0x3F;
outBuffer[3] = base64Char[order];
#if OPEN_DBG
cout<<order<<endl;
cout<<outBuffer[3]<<endl;
#endif
}
unsigned int Base64_Operation::_singleGroupDecode(char* inBuffer, char* outBuffer)
{
unsigned int resLen = 0;
unsigned char value[4];
unsigned int index = 0;
#if 1
for(index = 0;index<4;index++)
{
if((inBuffer[index]>= 'A') && (inBuffer[index]<='Z'))
{
}
else if((inBuffer[index] >= 'a') && (inBuffer[index]<='z'))
{
}
else if((inBuffer[index] >= '0') && (inBuffer[index]<='9'))
{
}
else if((inBuffer[index] == '+') || (inBuffer[index] == '/'))
{
}
else if(inBuffer[index] == '=')
{
if(index<=1)
return resLen;
}
else
{
return resLen;
}
}
#endif
value[0] = charDecTable[(unsigned char)inBuffer[0]];
value[1] = charDecTable[(unsigned char)inBuffer[1]];
value[2] = charDecTable[(unsigned char)inBuffer[2]];
value[3] = charDecTable[(unsigned char)inBuffer[3]];
if((value[2] == EQUAL_SIGN_MAGIC) && (value[3] == EQUAL_SIGN_MAGIC))
{
resLen = 1;
outBuffer[0] = ((value[0]&0x3F)<<2) | ((value[1]&0x30)>>4);
}
else if(value[3] == EQUAL_SIGN_MAGIC)
{
resLen = 2;
outBuffer[0] = ((value[0]&0x3F)<<2) | ((value[1]&0x30)>>4);
outBuffer[1] = ((value[1]&0x0F)<<4) | ((value[2]&0x3C)>>2);
}
else
{
resLen = 3;
outBuffer[0] = ((value[0]&0x3F)<<2) | ((value[1]&0x30)>>4);
outBuffer[1] = ((value[1]&0x0F)<<4) | ((value[2]&0x3C)>>2);
outBuffer[2] = ((value[2]&0x03)<<6) | (value[3]&0x3F);
}
return resLen;
}
Base64_Operation *Base64_Operation::base64_GetInstance()
{
if(Instance == NULL)
{
Instance = new Base64_Operation();
Instance->_initDecTable();
#if OPEN_DBG
int index = 0;
for(index = 0; index<=DECTABLE_LEN; index++)
{
cout<<index<<": "<<charDecTable[index]<<endl;
}
#endif
}
return Instance;
}
Base64_Operation::Base64_Operation()
{
cout<<"Create a base64 object"<<endl;
}
Base64_Operation::~Base64_Operation()
{
cout<<"Destroy a base64 object"<<endl;
Instance = NULL;
}
BASE64_RESULT Base64_Operation::base64_file_encode(FILE* pInFile, FILE* pOutFile)
{
if(pInFile == NULL || pOutFile == NULL)
{
cout<<__FUNCTION__<<" "<<__LINE__<<" The file pointer can't be NULL"<<endl;
return E_BASH64_FILE_ERROR;
}
unsigned int readLen = 0;
unsigned int outBufferLen = 0;
//The hexadecimal digit:0x1FFFFE can be devided by 3;
char *tmpBuffer = new char[0x1FFFFE];
if(tmpBuffer == NULL)
{
return E_BASH64_BUFFER_OVERFLOW;
}
char *outBuffer = new char[FILE_OUTTMP_LEN];
if(outBuffer == NULL)
{
delete []tmpBuffer;
return E_BASH64_BUFFER_OVERFLOW;
}
do
{
memset(tmpBuffer,0x00, 0x1FFFFE);
readLen = fread(tmpBuffer, 1, 0x1FFFFE, pInFile);
if(E_BASH64_OK == (this->base64_hex_encode(tmpBuffer, readLen, outBuffer, &outBufferLen)))
{
if(fwrite(outBuffer, outBufferLen, 1, pOutFile)!=1)
{
cout<<__FUNCTION__<<" write file error!"<<endl;
delete []tmpBuffer;
delete []outBuffer;
return E_BASH64_FILE_ERROR;
}
}
else
{
cout<<__FUNCTION__<<" encode base64 failed!"<<endl;
delete []tmpBuffer;
delete []outBuffer;
return E_BASH64_ERROR_USUAL;
}
}while(readLen==0x1FFFFE);
delete []tmpBuffer;
delete []outBuffer;
return E_BASH64_OK;
}
BASE64_RESULT Base64_Operation::base64_file_decode(FILE* pInFile, FILE* pOutFile)
{
if(pInFile == NULL || pOutFile == NULL)
{
cout<<__FUNCTION__<<" "<<__LINE__<<" The file pointer can't be NULL"<<endl;
return E_BASH64_FILE_ERROR;
}
unsigned int readLen = 0;
unsigned int outBufferLen = 0;
char *tmpBuffer = new char[FILE_BUFFER_LEN];
if(tmpBuffer == NULL)
{
return E_BASH64_BUFFER_OVERFLOW;
}
char *outBuffer = new char[FILE_OUTTMP_LEN];
if(outBuffer == NULL)
{
delete []tmpBuffer;
return E_BASH64_BUFFER_OVERFLOW;
}
do
{
memset(tmpBuffer,0x00, FILE_BUFFER_LEN);
readLen = fread(tmpBuffer, 1, FILE_BUFFER_LEN, pInFile);
if(E_BASH64_OK == (this->base64_hex_decode(tmpBuffer, readLen, outBuffer, &outBufferLen)))
{
if(fwrite(outBuffer, outBufferLen, 1, pOutFile)!=1)
{
cout<<__FUNCTION__<<" write file error!"<<endl;
delete []tmpBuffer;
delete []outBuffer;
return E_BASH64_FILE_ERROR;
}
}
else
{
cout<<__FUNCTION__<<" decode base64 failed!"<<endl;
delete []tmpBuffer;
delete []outBuffer;
return E_BASH64_ERROR_USUAL;
}
}while(readLen==FILE_BUFFER_LEN);
delete []tmpBuffer;
delete []outBuffer;
return E_BASH64_OK;
}
BASE64_RESULT Base64_Operation::base64_hex_encode(char* pInBuffer,unsigned int InBufferLen, char *pOutBuffer, unsigned int *pOutBufferLen)
{
if(pInBuffer == NULL || pOutBuffer == NULL || pOutBufferLen == NULL)
{
cout<<__FUNCTION__<<" "<<"The buffer can't be NULL"<<endl;
return E_BASH64_ERROR_PARAM;
}
char *pRunInBuffer = pInBuffer;
char *pRunOutBuffer = pOutBuffer;
char withoutEnc[3];
char withEnc[4];
unsigned int order = 0;
*pOutBufferLen = 0;
if(InBufferLen >= 3)
{
do
{
memcpy(withoutEnc, pRunInBuffer, 3);
this->_singleGroupEncode(withoutEnc, withEnc);
memcpy(pRunOutBuffer, withEnc, 4);
pRunOutBuffer = pRunOutBuffer + 4;
*pOutBufferLen = *pOutBufferLen + 4;
InBufferLen = InBufferLen-3;
pRunInBuffer = pRunInBuffer+3;
}while(InBufferLen>=3);
}
if(InBufferLen == 2)
{
order = (pRunInBuffer[0]&0xFC)>>2;
pRunOutBuffer[0] = base64Char[order];
order = ((pRunInBuffer[0]&0x3)<<4) | ((pRunInBuffer[1]&0xF0)>>4);
pRunOutBuffer[1] = base64Char[order];
order = (pRunInBuffer[1]&0xF)<<2;
pRunOutBuffer[2] = base64Char[order];
pRunOutBuffer[3] = '=';
*pOutBufferLen = *pOutBufferLen + 4;
}
else if(InBufferLen == 1)
{
order = (pRunInBuffer[0]&0xFC)>>2;
pRunOutBuffer[0] = base64Char[order];
order = (pRunInBuffer[0]&0x3)<<4;
pRunOutBuffer[1] = base64Char[order];
pRunOutBuffer[2] = '=';
pRunOutBuffer[3] = '=';
*pOutBufferLen = *pOutBufferLen + 4;
}
return E_BASH64_OK;
}
BASE64_RESULT Base64_Operation::base64_hex_decode(char* pInBuffer, unsigned int InBufferLen, char *pOutBuffer, unsigned int *pOutBufferLen)
{
if(pInBuffer == NULL || pOutBuffer == NULL || pOutBufferLen == NULL)
{
cout<<__FUNCTION__<<" "<<"The buffer can't be NULL"<<endl;
return E_BASH64_ERROR_PARAM;
}
if(InBufferLen%4)
{
cout<<__FUNCTION__<<" "<<"The inBufferLen must be devided by 4"<<endl;
return E_BASH64_ERROR_PARAM;
}
char *pRunInBuffer = pInBuffer;
char *pRunOutBuffer = pOutBuffer;
char withoutEnc[3];
char withEnc[4];
unsigned int resLen = 0;
*pOutBufferLen = 0;
// cout<<__FUNCTION__<<" "<<__LINE__<<endl;
while((InBufferLen/4))
{
memcpy(withEnc, pRunInBuffer, 4);
resLen = this->_singleGroupDecode(withEnc, withoutEnc);
memcpy(pRunOutBuffer, withoutEnc, resLen);
// cout<<__FUNCTION__<<" "<<__LINE__<<endl;
if((resLen<3) && (InBufferLen>4))
{
cout<<__FUNCTION__<<" wrong input string for decode!"<<endl;
return E_BASH64_ERROR_USUAL;
}
else if(resLen == 0)
{
cout<<__FUNCTION__<<" error base64 string!"<<endl;
return E_BASH64_ERROR_USUAL;
}
InBufferLen = InBufferLen-4;
pRunInBuffer = pRunInBuffer+4;
pRunOutBuffer = pRunOutBuffer+resLen;
*pOutBufferLen = *pOutBufferLen+resLen;
}
return E_BASH64_OK;
}
这是base64编码与解码的具体实现;
base64_main.cpp
#include <iostream>
#include <stdio.h>
#include <string.h>
#include "base64_operation.h"
using std::cin;
using std::cout;
using std::endl;
using std::hex;
#define TEST_FILEIN "./test_fileIn.bin"
#define TEST_FILEOUT "./test_fileOut.bin"
#define TEST_DECOUT "./test_dec.bin"
int main(int argc, char* argv[])
{
FILE* pInFile = NULL;
FILE* pOutFile = NULL;
char *pOutBuffer = new char[100];
unsigned int outBufferLen = 0;
BASE64_RESULT eRet = E_BASH64_ERROR_USUAL;
Base64_Operation* pBase64 = Base64_Operation::base64_GetInstance();
if(pBase64 == NULL)
{
cout<<__FUNCTION__<<"Get base64 instance failed!"<<endl;
goto DESTROY;
}
//memset(pOutBuffer, '\0', 100);
eRet = pBase64->base64_hex_encode((char*)"s13sdfasdf*we", 13, pOutBuffer, &outBufferLen);
if(eRet == E_BASH64_OK)
{
cout<<"outBufferLen is: "<<outBufferLen<<endl;
cout<<pOutBuffer<<endl;
// delete []pOutBuffer;
// goto DESTROY;
}
memset(pOutBuffer, '\0', 100);
eRet = pBase64->base64_hex_decode((char*)"czEzc2RmYXNkZip3ZQ==",0x14, pOutBuffer, &outBufferLen);
if(eRet == E_BASH64_OK)
{
cout<<"outBufferLen is: "<<outBufferLen<<endl;
cout<<pOutBuffer<<endl;
// goto DESTROY;
}
if((pInFile=fopen((const char*)TEST_FILEIN, "rb")) == NULL)
{
cout<<"source file open failed!"<<endl;
return -1;
}
if((pOutFile=fopen((const char*)TEST_FILEOUT, "wb+")) == NULL)
{
cout<<"output file open failed!"<<endl;
return -1;
}
eRet = pBase64->base64_file_encode(pInFile, pOutFile);
if(eRet == E_BASH64_OK)
{
cout<<"file encode successfully!"<<endl;
fclose(pInFile);
fclose(pOutFile);
pInFile = NULL;
pOutFile = NULL;
}
if((pInFile=fopen((const char*)TEST_FILEOUT, "rb")) == NULL)
{
cout<<"source file open failed!"<<endl;
return -1;
}
if((pOutFile=fopen((const char*)TEST_DECOUT, "wb+")) == NULL)
{
cout<<"output file open failed!"<<endl;
return -1;
}
eRet = pBase64->base64_file_decode(pInFile, pOutFile);
if(eRet == E_BASH64_OK)
{
cout<<"file dec successfully!"<<endl;
fclose(pInFile);
fclose(pOutFile);
pInFile = NULL;
pOutFile = NULL;
}
DESTROY:
if(pBase64 != NULL)
{
delete pBase64;
pBase64 = NULL;
}
return 0;
}
这是对应的测试函数,可在linux有g++环境下如果编译测试:
g++ base64_main.cpp base64_operaion.cpp -o base64
然后运行:
./base64
正常情况下会有下面打印输出:
Create a base64 object
outBufferLen is: 20
czEzc2RmYXNkZip3ZQ==
outBufferLen is: 13
s13sdfasdf*we
file encode successfully!
file dec successfully!
Destroy a base64 object