Base64编码/解码[C/C++]

#include <windows.h>
#include <cstdint>
#include <cassert>
#include <iostream>
#include <clocale>

#define _TEST

const char base64Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                           "abcdefghijklmnopqrstuvwxyz"
                           "0123456789+/";

/*

    \\ Base64编码
    //
    // 3字节为一组处理
    // 3字节元数据 -> 编码后为4字节, 不足3字节的以0补足
    //
    // 比如:
    // 待编码字符串:             hello
    // 转换为二进制:             01101000  01100101  01101100  01101100  01101111 00000000
    // 以6位为单位转换为十进制:   26    6       21      44      27    6       60     0
    // 以转换出来的数字索引编码表: a     G       V       s       b     G       8      A
    // 按比较常见的MIME BASE64编码规范的话, 最后填补的 'A' 一般用 '=' 代替
    // 最终结果就是:  aGVsbG8=
    //
    
*/
void Base64Encode(
    IN const char *pInData,
    IN uint32_t uLen,
    OUT char **pOut,
    OUT uint32_t *uLenOut)
{
    assert(pInData && uLen > 0);

    #ifdef _TEST
    for(int i = 0; i < uLen; ++i)
        printf("%x ", pInData[i]);
    putc('\n', stdout);
    #endif

    // 计算所需输出空间
    uint32_t div = uLen / 3;
    uint32_t mod = uLen % 3;
    // 针对字符串数据+1
    *uLenOut = (div + (mod == 0 ? 0 : 1)) * 4 + 1;

    // 开辟新空间
    *pOut = (char*)calloc(*uLenOut, 1);
    char *pTmp = *pOut;

    // 填充新空间, 每3个原字节转化为新的4个字节数据
    int iTmp = 0;
    for (int i = 0, j = 0; i < uLen; i += 3, j += 4)
    {
        iTmp = uLen - i;
        if (iTmp >= 3)
        {
            pTmp[j] = base64Table[pInData[i] >> 2];
            pTmp[j + 1] = base64Table[((pInData[i] & 0x3) << 4) | (pInData[i + 1] >> 4)];
            pTmp[j + 2] = base64Table[((pInData[i + 1] & 0xF) << 2) | ((pInData[i + 2] & 0xC0) >> 6)];
            pTmp[j + 3] = base64Table[pInData[i + 2] & 0x3F];
        }
        else
        {
            // 刚好是3的倍数则不进入处理
            // 处理 总数 % 3 的字节( 1 <= x <= 2 )
            pTmp[j] = base64Table[pInData[i] >> 2];
            if (iTmp == 2)
            {
                pTmp[j + 1] = base64Table[((pInData[i] & 0x3) << 4) | (pInData[i + 1] >> 4)];
                pTmp[j + 2] = base64Table[(pInData[i + 1] & 0xF) << 2];
            }
            else
            {
                pTmp[j + 1] = base64Table[(pInData[i] & 0x3) << 4];
                pTmp[j + 2] = '=';
            }

            pTmp[j + 3] = '=';
        }
    }

    pTmp[*uLenOut - 1] = '\0';
    // 实际长度减掉1
    *uLenOut -= 1;
}



/*

    \Base64解码

*/
void Base64Decode(
    IN const char *pEncode,
    IN uint32_t uEncLen,
    OUT char **pDecode,
    OUT uint32_t *uDecLen)
{
    assert(pEncode && uEncLen > 0);

    // 先将编码数据转换为索引表中的索引
    char *uIndexs = (char *)calloc(uEncLen, 1);

    int i, j;
    char cTmp;
    for (i = 0; i < uEncLen; i++)
    {
        cTmp = pEncode[i];

        /* = */
        if (cTmp == '=')
            uIndexs[i] = 0;
        /* + / */
        else if (cTmp < '0')
            uIndexs[i] = (cTmp == '/' ? 63 : 62);
        /* 0123456789 */
        else if (cTmp <= '9')
            uIndexs[i] = 52 + (cTmp - '0');
        /* A-Z */
        else if (cTmp <= 'Z')
            uIndexs[i] = cTmp - 'A';
        /* a-z */
        else if (cTmp <= 'z')
            uIndexs[i] = 26 + cTmp - 'a';
    }

    // 现在得到了原来编码时用的索引值列表
    // 因为编码时是在原数据上按6bit的单位依次取得的索引
    // 所以现在的每个索引应该去掉高位2bit然后再组装成原数据就行了

    // 计算所需空间
    *uDecLen = (uEncLen / 4) * 3 + 1;
    *pDecode = (char *)calloc(*uDecLen, 1);
    char *pTmp = *pDecode;

    // 解码按4个索引对3个原字节来分组处理
    for (i = 0, j = 0; i < uEncLen; i += 4, j += 3)
    {
        /* 每组第一个索引后6位 + 第二个索引去高两位后的次高2位 */
        pTmp[j] = (uIndexs[i] << 2) | ((uIndexs[i + 1] >> 4) & 0x3);
        /* 每组第二个索引余下的低4位 + 第三个索引的去高两位后的次高4位*/
        pTmp[j + 1] = ((uIndexs[i + 1] & 0xf) << 4) | ((uIndexs[i + 2] >> 2) & 0xF);
        /* 每组第三个索引的低2位 + 第四个索引的低6位 */
        pTmp[j + 2] = ((uIndexs[i + 2] & 0x3) << 6) | (uIndexs[i + 3] & 0x3F);
    }

    free(uIndexs);
}



// MAIN
int wmain(int argc, wchar_t **argv)
{
    assert(argc == 2);

    char *lc = setlocale(LC_ALL, "chs");
    uint32_t uLenIn = wcsnlen(argv[1], MAX_PATH);
    uint32_t uLenOut = 0;

    // 编码
    char *pEncOut = NULL;
    // 因为用了UNICODE的U16_LE字符编码方式, 
    // wcslen 返回的是字符个数, 无论中英文, 而不是字节数, 而wchar_t类型1个字符用两个字节表示, 所以长度 *2
    // 第二个参数是字节数不是字符数. 函数是面向字节流的.
    Base64Encode((const char*)argv[1], uLenIn*2, &pEncOut, &uLenOut);
    if (pEncOut)
    {
        std::cout << "Encrypt: " << pEncOut << std::endl;
        // 解码
        char *pDecOut = NULL;
        Base64Decode(pEncOut, uLenOut, &pDecOut, &uLenOut);
        if (pDecOut)
        {
            std::wcout << L"Decrypted: " << ((wchar_t*)pDecOut) << std::endl;
            free(pDecOut);
        }
        else
        {
            std::wcout << L"Base64Decode failed\n";
        }

        free(pEncOut);
    }
    else
    {
        std::wcout << L"Base64Encode failed\n";
    }

    setlocale(LC_ALL, lc);
    return 0;
}

编码原理图示(来源):

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值