21
2017
09

base64的C++实现

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

## 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
上一篇:p2469,[sdoi2010]星际竞速 下一篇:Java选择和冒泡排序