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;

    //末尾增加\0
    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] = '\0';
    *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 多留一個字符'\0'
    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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM