zoukankan      html  css  js  c++  java
  • Linux共享库 base64库

        base64网上实现很多,但是如果是对中文进行编码,有的无法编码,有的编码结果不一致

    经过研究,发现base64算法都没有问题,问题出现在汉字的编码上,下面的base64编码稍微做了一些改进,增加了编码判断

    所有汉字一律转换成UTF8后再进行base64编码,与网络上通用的base64解码接轨。

        以下base64算法使用了开源库uchardet,需要下载uchardet源码编译生成动态库调用

        uchardet源码网址:官网地址

    /**
     * base64编码原理
     * 1. 源数据都是8位位宽的数据;
     * 2. 相当于分组码,将源数据分为3个一组,每一组共24bits,采用每6位对应一个编码码字,那么3*8bits = 4*6its,将3个数据映射成4个数据,
          由于编码的码字都是6位长度,换位10进制就是0-63,总共有64中可能性,这也是base64名字的来历;
     * 3. 6bits对应10进制数对应的码字如表;[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/]
     *  转解码过程
     *  3 * 8 = 4 * 6; 3字节占24位, 4*6=24
     *  先将要编码的转成对应的ASCII值
     *  如编码: s 1 3
     *  对应ASCII值为: 115 49 51
     *  对应二进制为: 01110011 00110001 00110011
     *  将其6个分组分4组: 011100 110011 000100 110011
     *  而计算机是以8bit存储, 所以在每组的高位补两个0如下:
     *  00011100 00110011 00000100 00110011对应:28 51 4 51
     *  查找base64 转换表 对应 c z E z
     *  
     *  解码
     *  c z E z
     *  对应ASCII值为 99 122 69 122
     *  对应表base64_suffix_map的值为 28 51 4 51
     *  对应二进制值为 00011100 00110011 00000100 00110011
     *  依次去除每组的前两位, 再拼接成3字节
     *  即: 01110011 00110001 00110011
     *  对应的就是s 1 3
     */
    #ifndef __BASE64_H_
    #define __BASE64_H_
    
    /********************************************************
       Func Name: base64_encode
    Date Created: 2018-8-3
     Description: base64编码
           Input: plaintext_in:源文件
                     length_in:源文件长度
          Output:     code_out:生成编码文件
                    length_out:生成编码文件的长度
          Return: 
         Caution: code_out内存由调用函数释放
    *********************************************************/
    int base64_encode(const char *plaintext_in, int length_in, char **code_out, int *length_out);
    
    /********************************************************
       Func Name: base64_decode
    Date Created: 2018-8-3
     Description: base64解码
           Input:       code_in;编码后的文件
                      length_in:编码后的文件长度
          Output: plaintext_out:源文件
                         outlen:源文件长度
          Return: 
         Caution: plaintext_out内存由调用函数释放
    *********************************************************/
    int base64_decode(char *code_in, int length_in, char **plaintext_out, int *outlen);
    
    /********************************************************
       Func Name: base64_encode_calculate
    Date Created: 2018-8-2
     Description: 编码算法
           Input: plaintext_in:源文件
                     length_in:源文件长度
          Output:     code_out:生成编码文件
                    length_out:生成编码文件的长度
          Return: 
         Caution: code_out内存由调用函数释放
    *********************************************************/
    int base64_encode_calculate(const char *plaintext_in, int length_in, char **code_out, int *length_out);
    
    /********************************************************
       Func Name: base64_decode_calculate
    Date Created: 2018-8-3
     Description: 解码算法
           Input:       code_in;编码后的文件
                      length_in:编码后的文件长度
          Output: plaintext_out:源文件
                         outlen:源文件长度
          Return: 
         Caution: plaintext_out内存由调用函数释放
    *********************************************************/
    int base64_decode_calculate(char *code_in, int length_in, char **plaintext_out, int *outlen);
    
    #endif
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "base64.h"
    #include "ucharcode.h"
    
    #define SRC_CHAR_SIZE 3                //源码3个字节
    #define BASE_CHAR_SIZE 4               //编码后4个字节
    #define CHAR_SIZE 8                    //一个字节有8bits
    #define BASE_DATA_SIZE 6               //base编码中6个bits是实际数据
    
    #define DEFAULT_CODE "UTF-8"
    
    
    /********************************************************
       Func Name: base64_encode_value
    Date Created: 2018-8-2
     Description: 获取对应编码的值
           Input: value_in:需要编码的字符
          Output: 
          Return: 
         Caution: 
    *********************************************************/
    char base64_encode_value(char value_in)
    {
        static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        if (value_in > 63) return '=';
        return encoding[(int)value_in];
    }
    
    /********************************************************
       Func Name: base64_decode_value
    Date Created: 2018-8-2
     Description: 获取对应解码的值
           Input: value_in:需要解码的字符
          Output: 
          Return: 
         Caution: 
    *********************************************************/
    int base64_decode_value(char value_in)
    {
        static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56
            ,57,58,59,60,61,-1,-1,-1,-2,-1
            ,-1,-1,0,1,2,3,4,5,6,7
            ,8,9,10,11,12,13,14,15,16,17
            ,18,19,20,21,22,23,24,25,-1,-1
            ,-1,-1,-1,-1,26,27,28,29,30,31
            ,32,33,34,35,36,37,38,39,40,41
            ,42,43,44,45,46,47,48,49,50,51};
    
        static const char decoding_size = sizeof(decoding);
        //+ 的ascll值是43
        value_in -= 43;
        if (value_in < 0 || value_in >= decoding_size) return -1;
        return decoding[(int)value_in];
    }
    
    /********************************************************
       Func Name: base64_encode
    Date Created: 2018-8-3
     Description: base64编码
           Input: plaintext_in:源文件
                     length_in:源文件长度
          Output:     code_out:生成编码文件
                    length_out:生成编码文件的长度
          Return: 
         Caution: code_out内存由调用函数释放
    *********************************************************/
    int base64_encode(const char *plaintext_in, int length_in, char **code_out, int *length_out)
    {
        int iRet = 0;
        char * pcCode = NULL;
        char *pcOut = NULL;
        //因为utf8一个汉字用3个字节表示,gdk用两个字节表示,所以2倍的长度应该是够用的
        int iOutLen = length_in * 2;
        int iLeftNum = iOutLen;
        //参数校验
        if (NULL == plaintext_in || 0 == length_in || NULL == code_out || NULL == length_out)
        {
            return -1;
        }
        //编码格式判断
        iRet = getStringCode(plaintext_in, length_in, &pcCode);
        if(0 != iRet)
        {
            return -2;
        }
        if (0 == strcmp(pcCode,DEFAULT_CODE))
        {
            iRet = base64_encode_calculate(plaintext_in, length_in, code_out, length_out);
            return iRet;
        }
        //如果不是UTF-8编码 需要转码
        pcOut = (char *)malloc(iOutLen);
        if (NULL == pcOut)
        {
            return -3;
        }
        memset(pcOut, 0, iOutLen);
        iRet = transcodeToUTF8((char *)plaintext_in, length_in, pcOut, &iLeftNum, pcCode);
        if (0 != iRet)
        {
            //释放资源
            if (pcOut)
            {
                free(pcOut);
                pcOut = NULL;
            }
            return -4;
        }
    
        //释放编码资源
        if (pcCode)
        {
            free(pcCode);
            pcCode = NULL;
        }
        iRet = base64_encode_calculate(pcOut, iOutLen - iLeftNum, code_out, length_out);
    
        //释放资源
        if (pcOut)
        {
            free(pcOut);
            pcOut = NULL;
        }
    
        return iRet;
    
    }
    
    /********************************************************
       Func Name: base64_encode_calculate
    Date Created: 2018-8-2
     Description: 编码算法
           Input: plaintext_in:源文件
                     length_in:源文件长度
          Output:     code_out:生成编码文件
                    length_out:生成编码文件的长度
          Return: 
         Caution: code_out内存由调用函数释放
    *********************************************************/
    int base64_encode_calculate(const char *plaintext_in, int length_in, char **code_out, int *length_out)
    {
        int iPadLen = 0;                 //需要补齐的字节数
        int iBaseLen = 0;                //base64编码后的字节数
        int i = 0;
        char *pcOut = NULL;
        char gPadChar[BASE_CHAR_SIZE] = {0};
        char * pcOutIndex = NULL;
    
        if (NULL == plaintext_in || 0 == length_in || NULL == code_out || NULL == length_out)
        {
            return -1;
        }
    
        if (0 != length_in % SRC_CHAR_SIZE)
        {
            //3 - length_in%3 源码需要添加的字节数
            iPadLen = SRC_CHAR_SIZE - length_in % SRC_CHAR_SIZE;
        }
        //计算base编码后实际长度 +1 最后一个字符是'/0'
        iBaseLen = (length_in + iPadLen)* CHAR_SIZE / BASE_DATA_SIZE + 1;
    
        pcOut = (char *)malloc(sizeof(char) * iBaseLen);
        if (NULL == pcOut)
        {
            return -2;
        }
        memset(pcOut, 0, sizeof(char) * iBaseLen);
        pcOutIndex = pcOut;
    
        for (i = 0; i < length_in; i += SRC_CHAR_SIZE)
        {
            if (i == (length_in + iPadLen -3) && 0 != iPadLen)
            {
                if (1 == iPadLen)
                {
                    //末尾实际上有两个字节的数据,将这两个字节的数据进行编码
    
                    //第一个字节处理
                    gPadChar[0] = base64_encode_value(*(plaintext_in+i) >> 2 & 0x3f);
    
                    //第二个字节处理
                    gPadChar[1] = base64_encode_value((*(plaintext_in+i) << 6 >> 2 & 0x30) + (*(plaintext_in+i+1) >> 4 & 0xf));
    
                    //第二个字节最后4bits处理,不足的补0
                    gPadChar[2] = base64_encode_value((*(plaintext_in+i+1) << 4 >> 2 & 0x3c));
    
                    //没有字节的以'='代替 因为base编码只有6bit真实数据,所以不会和源数据中的"="重复
                    gPadChar[3] = '=';
    
                }else if (2 == iPadLen)
                {
                    //末尾实际上有一个字节的数据,将这一个字节的数据进行编码
                    //第一个字节处理
                    gPadChar[0] = base64_encode_value(*(plaintext_in+i) >> 2 & 0x3f);
    
                    //第一个字节最后2bits处理
                    gPadChar[1] = base64_encode_value((*(plaintext_in+i) << 6 >> 2 & 0x30));
    
                    gPadChar[2] = '=';
                    gPadChar[3] = '=';
                }
                
            }else
            {
                //第一个字节处理  0x3f前两位清零
                //取第一个字节的前6bits
                gPadChar[0] =base64_encode_value(*(plaintext_in+i) >> 2 & 0x3f);
    
                //第二个字节处理
                //*(pcIndex+i) << 6 >> 2 & 0x30 取第一个字节的后2bits 无效数据清零
                //*(pcIndex+i+1) >> 4 & 0xf 取第二个字节的前4bits 无效数据清零
                gPadChar[1] = base64_encode_value((*(plaintext_in+i) << 6 >> 2 & 0x30) + (*(plaintext_in+i+1) >> 4 & 0xf));
    
                //第二个字节和第三个字节处理
                //*(pcIndex+i+1) << 4 >> 2 & 0x3c 取第二个字节的后4bits数据 无效数据清零
                //(*(pcIndex+i+2) >> 6 & 0x3 取第三个字节的前2bits数据 无效数据清零
                gPadChar[2] = base64_encode_value((*(plaintext_in+i+1) << 4 >> 2 & 0x3c) + (*(plaintext_in+i+2) >> 6 & 0x3));
    
                //第三个字节处理
                //*(pcIndex+i+2) & 0x3f  清除第三个字节的前2bits数据
                gPadChar[3] = base64_encode_value(*(plaintext_in+i+2) & 0x3f);
            }
            
    
            //并非字符串操作,不能使用strcat
            memcpy(pcOutIndex, gPadChar, BASE_CHAR_SIZE);
            pcOutIndex += BASE_CHAR_SIZE;
    
            memset(gPadChar, 0, BASE_CHAR_SIZE);
        }
    
        pcOut[iBaseLen-1] = 0;
        *length_out = iBaseLen;
        *code_out = pcOut;
    
        return 0;
    }
    
    /********************************************************
       Func Name: base64_decode
    Date Created: 2018-8-3
     Description: base64解码
           Input:       code_in;编码后的文件
                      length_in:编码后的文件长度
          Output: plaintext_out:源文件
                         outlen:源文件长度
          Return: 
         Caution: plaintext_out内存由调用函数释放
    *********************************************************/
    int base64_decode(char *code_in, int length_in, char **plaintext_out, int *outlen)
    {
        int iRet = base64_decode_calculate(code_in, length_in, plaintext_out, outlen);
        return iRet;
    }
    
    /********************************************************
       Func Name: base64_decode_calculate
    Date Created: 2018-8-3
     Description: 解码算法
           Input:       code_in;编码后的文件
                      length_in:编码后的文件长度
          Output: plaintext_out:源文件
                         outlen:源文件长度
          Return: 
         Caution: plaintext_out内存由调用函数释放
    *********************************************************/
    int base64_decode_calculate(char *code_in, int length_in, char **plaintext_out, int *outlen)
    {
        int i = 0, j = 0;
        int iPadNum = 0;
        char *pcSrc = code_in;
        char * pcIndex = NULL;
        int iSrcLen = 0;
        char *pcOut = NULL;
    
        if (NULL == code_in || NULL == plaintext_out || NULL == outlen)
        {
            return -1;
        }
    
        while(1)
        {
            pcIndex = strchr(pcSrc, '=');
            if (NULL == pcIndex)
            {
                break;
            }
            iPadNum++;
            pcIndex += 1;
            pcSrc = pcIndex;
    
        }
        //计算源文件的字符个数
        iSrcLen = length_in/4*3 - iPadNum;
    
        //末尾增加
        pcOut = (char *)malloc(sizeof(char)*iSrcLen + 1);
        if (NULL == pcOut)
        {
            return -2;
        }
        memset(pcOut, 0, sizeof(char)*iSrcLen + 1);
    
        for (i = 0, j = 0; i < length_in; i += 4)
        {
            if ((i == length_in-4) && iPadNum > 0)
            {
                if (1 == iPadNum)
                {
                    //实际上有两个字符 只能用3个base字符表示
    
                    //字符1
                    pcOut[j] = (base64_decode_value(code_in[i]) << 2) + (base64_decode_value(code_in[i+1]) << 2 >> 6 & 0x3);
                    //字符2
                    pcOut[j+1] = (base64_decode_value(code_in[i+1]) << 4) + (base64_decode_value(code_in[i+2]) << 2 >> 4 & 0xf);
                    j += 2;
                }else if (2 == iPadNum)
                {
                    //实际上有1个字符数据 只能用2个base字符表示
                    pcOut[j] = (base64_decode_value(code_in[i])<<2) + (base64_decode_value(code_in[i+1]) << 2 >> 6 &0x3);
                    j ++;
                }
            }else
            {
                //字符1
                pcOut[j] = (base64_decode_value(code_in[i])<<2) + (base64_decode_value(code_in[i+1]) << 2 >> 6 &0x3);
                //字符2
                pcOut[j+1] = (base64_decode_value(code_in[i+1]) << 4) + (base64_decode_value(code_in[i+2]) << 2 >> 4 & 0xf);
                //字符3
                pcOut[j+2] = (base64_decode_value(code_in[i+2]) << 6) + (base64_decode_value(code_in[i+3]) & 0x3f);
                j += 3;
            }
        }
    
        pcOut[iSrcLen] = '';
        *plaintext_out = pcOut;
        *outlen = iSrcLen;
    
        return 0;
    }
    #ifndef __UNCHARCODE_H_
    #define __UNCHARCODE_H_
    
    /********************************************************
       Func Name: getStringCode
    Date Created: 2018-8-3
     Description: 获取字符串编码
           Input: pcSrc:源编码数据
                 iLenIn:源编码长度
                 pcCode:结果存放内存地址
          Output:         
          Return: error code
         Caution: pcDest内存需要由调用函数释放
    *********************************************************/
    int getStringCode(const char *pcSrc, int iLenIn, char **pcCode);
    
    /********************************************************
       Func Name: transcodeToUTF8
    Date Created: 2018-8-3
     Description: 转码UTF8
           Input: pcSrc:源编码数据
                  lenIn:源编码长度
                 pcDest:结果存放内存地址(INOUT)
                 lenOut:剩余内存单位个数(INOUT)
             pcCodeType:源编码类型
          Output:         
          Return: error code
         Caution: pcDest内存需要由调用函数分配
    *********************************************************/
    int transcodeToUTF8(char *pcSrc, int lenIn, char *pcDest, int *lenOut,const char *pcCodeType);
    
    /********************************************************
       Func Name: transcodeToGBK
    Date Created: 2018-8-3
     Description: 转码GBK
           Input: pcSrc:源编码数据
                  lenIn:源编码长度
                 pcDest:结果存放内存地址(INOUT)
                 lenOut:剩余内存单位个数(INOUT)
             pcCodeType:源编码类型
          Output:         
          Return: error code
         Caution: pcDest内存需要由调用函数分配
    *********************************************************/
    int transcodeToGBK(char *pcSrc, int lenIn, char *pcDest, int *lenOut,const char *pcCodeType);
    
    #endif
    #include <stdlib.h>
    #include <string.h>
    #include <iconv.h>
    
    #include "ucharcode.h"
    #include "uchardet.h"
    
    /********************************************************
       Func Name: transcodeToUTF8
    Date Created: 2018-8-3
     Description: 转码UTF8
           Input: pcSrc:源编码数据
                  lenIn:源编码长度
                 pcDest:结果存放内存地址(INOUT)
                 lenOut:剩余内存单位个数(INOUT)
             pcCodeType:源编码类型
          Output:         
          Return: error code
         Caution: pcDest内存需要由调用函数分配
    *********************************************************/
    int fromcodeToCode(char *pcSrc, int lenIn, char *pcDest, int *lenOut, const char *pcFromCode, const char *pcToCode)
    {
        int iRet = 0;
    
        //由于iconv()函数会修改指针,所以要保存源指针
        char *pcStart = pcSrc;
        char *pcOut = pcDest;
    
        iconv_t cd = (iconv_t)-1;
        size_t inbytesleft = (size_t)lenIn;
    
        if (NULL == pcSrc || 0 >= lenIn || NULL == pcDest || NULL == lenOut || NULL == pcFromCode || NULL == pcToCode)
        {
            return -1;
        }
    
        cd = iconv_open(pcToCode, pcFromCode);
        if ((iconv_t)-1 == cd)
        {
            return -2;
        }
    
        /* 
           *@param cd iconv_open()产生的句柄
           *@param pcStart 需要转换的字符串
           *@param inbytesleft 存放还有多少字符没有转换
           *@param pcOut 存放转换后的字符串
           *@param outlen 存放转换后,pcOut剩余的空间
        */
        iRet = iconv(cd, &pcStart, &inbytesleft, &pcOut, (size_t *)lenOut);
        if (iRet < 0)
        {
            return -3;
        }
    
        iconv_close(cd);
    
        return iRet;
    }
    
    /********************************************************
       Func Name: transcodeToUTF8
    Date Created: 2018-8-3
     Description: 转码UTF8
           Input: pcSrc:源编码数据
                  lenIn:源编码长度
                 pcDest:结果存放内存地址(INOUT)
                 lenOut:剩余内存单位个数(INOUT)
             pcCodeType:源编码类型
          Output:         
          Return: error code
         Caution: pcDest内存需要由调用函数分配
    *********************************************************/
    int transcodeToUTF8(char *pcSrc, int lenIn, char *pcDest, int *lenOut,const char *pcCodeType)
    {
        int iRet = 0;
    
        const char * targetCode = "UTF8";
        if (NULL == pcSrc || 0 >= lenIn || NULL == pcDest || NULL == lenOut || NULL == pcCodeType)
        {
            return -1;
        }
    
        iRet = fromcodeToCode(pcSrc, lenIn, pcDest, lenOut, pcCodeType, targetCode);
    
        return iRet;
    }
    
    /********************************************************
       Func Name: transcodeToGBK
    Date Created: 2018-8-3
     Description: 转码GBK
           Input: pcSrc:源编码数据
                  lenIn:源编码长度
                 pcDest:结果存放内存地址(INOUT)
                 lenOut:剩余内存单位个数(INOUT)
             pcCodeType:源编码类型
          Output:         
          Return: error code
         Caution: pcDest内存需要由调用函数分配
    *********************************************************/
    int transcodeToGBK(char *pcSrc, int lenIn, char *pcDest, int *lenOut, char *pcCodeType)
    {
        int iRet = 0;
    
        const char * targetCode = "GBK";
        if (NULL == pcSrc || 0 >= lenIn || NULL == pcDest || NULL == lenOut || NULL == pcCodeType)
        {
            return -1;
        }
    
        iRet = fromcodeToCode(pcSrc, lenIn, pcDest, lenOut, pcCodeType, targetCode);
    
        return iRet;
    }
    
    /********************************************************
       Func Name: getStringCode
    Date Created: 2018-8-3
     Description: 获取字符串编码
           Input: pcSrc:源编码数据
                 iLenIn:源编码长度
                 pcCode:结果存放内存地址
          Output:         
          Return: error code
         Caution: pcDest内存需要由调用函数释放
    *********************************************************/
    int getStringCode(const char *pcSrc, int iLenIn, char **pcCode)
    {
        uchardet_t ud;
        int iErrorCode = 0;
        char *pcBuf = NULL;
        const char *pcDest = NULL;
    
        if (NULL == pcSrc || 0 == iLenIn || NULL == pcCode)
        {
            return -1;
        }
    
        ud = uchardet_new();
    
        //如果样本字符不够,那么有可能导致分析失败
        iErrorCode = uchardet_handle_data(ud, pcSrc, iLenIn);
        if (0 != iErrorCode)
        {
            return -2;
        }
    
        uchardet_data_end(ud);
    
        pcDest = uchardet_get_charset(ud);
    
        //+1 多留一个字符''
        pcBuf = (char *)malloc(strlen(pcDest)+1);
        if (NULL == pcBuf)
        {
            return -3;
        }
        memset(pcBuf, 0, strlen(pcDest)+1);
    
        strcpy(pcBuf,pcDest);
    
        *pcCode = pcBuf;
    
        uchardet_delete(ud);
    
        return 0;
    }
    #include <iostream>
    #include <cstring>
    
    #include "base64.h"
    
    using namespace std;
    
    /* 测试小程序 */
    
    void test()
    {
        int len = 0;
        int iRet= 0;
        char *pcOut = NULL;
    
        const char* s = "我是中国人";
        char * pcBuf = NULL;
        int lastLen = 0;
    
        //base64编码
        iRet = base64_encode((char *)s, strlen(s), &pcOut, &len);
        if (0 != iRet)
        {
            cout << "base64_encode() error ." << endl;
        }
        cout << "result = " << pcOut << endl;
    
        //base64解码
        iRet = base64_decode(pcOut, len, &pcBuf, &lastLen);
        if (0 != iRet)
        {
            cout << "base64_decode() error ." << endl;
        }
        cout << "end len = " << lastLen << "  result = " << pcBuf << endl;
        if (pcOut)
        {
            free(pcOut);
            pcOut = NULL;
        }
        if (pcBuf)
        {
            free(pcBuf);
            pcBuf = NULL;
        }
    }
    
    int main()
    {
        test();
        getchar();
        return 0;
    }
  • 相关阅读:
    jstack使用-倒出线程堆栈
    sentos nginx安装
    阿里云主机添加云磁盘
    redis 安装并设置为开机启动服务
    curl命令使用小结[转]
    FormView控件下DropDownList是否可以绑定
    数据绑定之DataFormatString
    GridView控件RowDataBound事件中获取列字段值的几种途径 !!!
    怎么用SQL语句备份和恢复数据库?
    XtraReports 入门教程
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/9416958.html
Copyright © 2011-2022 走看看