遇到一个问题:要将一个指定长度的char数组的图片数据通过网络传输,但是所使用的网络数据协议是字符类型的。所以char图片数组里有可能含有\0等字符,会影响到数据的解码。这是就用到了Base64算法。
先来看一下Base64的介绍(http://zh.wikipedia.org/wiki/Base64)
在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+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。
下面是一个简单的实现:
union { struct { unsigned long d: 6; unsigned long c: 6; unsigned long b: 6; unsigned long a: 6; } s; unsigned char uch[3]; } stUnit; char* strBase = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int MyEncodeBase64(const char *pInput, const int iInputLen, char *pOutput) { int iCur = 0; int iOutputLen = 0; int n = 0; while (1) { stUnit.uch[0] = stUnit.uch[1] = stUnit.uch[2] = 0; for (n = 0; n < 3; n++) { if (iInputLen == iCur) { break; } stUnit.uch[2 - n] = (unsigned char)pInput[iCur++]; } if (n == 0) { break; } switch (n) { case 1: pOutput[iOutputLen++] = strBase[stUnit.s.a]; pOutput[iOutputLen++] = strBase[stUnit.s.b]; pOutput[iOutputLen++] = '='; pOutput[iOutputLen++] = '='; break; case 2: pOutput[iOutputLen++] = strBase[stUnit.s.a]; pOutput[iOutputLen++] = strBase[stUnit.s.b]; pOutput[iOutputLen++] = strBase[stUnit.s.c]; pOutput[iOutputLen++] = '='; break; case 3: pOutput[iOutputLen++] = strBase[stUnit.s.a]; pOutput[iOutputLen++] = strBase[stUnit.s.b]; pOutput[iOutputLen++] = strBase[stUnit.s.c]; pOutput[iOutputLen++] = strBase[stUnit.s.d]; break; } } return iOutputLen; }
//下面是解析的: const BYTE Base64IdxTab[128] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, 255, 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, 255, 255, 255, 255, 255, 255, 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, 255, 255, 255, 255, 255 }; #define BVal(x) Base64IdxTab[x] int DecodeBase64(char *pInput, const int iInputLen, char *pOutput) { int i = 0; int iCnt = 0; int iSrcLen = iInputLen; //(int)strlen(pInput); char *p = pOutput; for (i = 0; i < iSrcLen; i++) { if (pInput[i] > 127) { continue; } if (pInput[i] == '=') { return p - pOutput + 1; } BYTE a = BVal(pInput[i]); if (a == 255) { continue; } switch (iCnt) { case 0: { *p = a << 2; iCnt++; } break; case 1: { *p++ |= a >> 4; *p = a << 4; iCnt++; } break; case 2: { *p++ |= a >> 2; *p = a << 6; iCnt++; } break; case 3: { *p++ |= a; iCnt = 0; } break; } } *p = 0x00; return p - pOutput; }
char buffer[256 * 1024] = {0}; FILE *pFile; long lSize; size_t result; pFile = fopen ("a.jpg" , "r" ); // obtain file size: fseek (pFile , 0 , SEEK_END); lSize = ftell (pFile); rewind (pFile); result = fread (buffer, 1, lSize, pFile); fclose(pFile); printf("PicSize:%d.\n", result); char pOutputBuff[256 * 1024 * 2] = {0}; int iOutLen = 0; iOutLen = MyEncodeBase64(buffer, lSize, pOutputBuff); /// printf("\nEncdoeLen %d.\n", iOutLen); //fwrite char pic_file_name[255]; snprintf(pic_file_name, 255, "./encode%03u.txt", 1); FILE *fp = fopen(pic_file_name, "w"); fwrite(pOutputBuff, iOutLen, 1, fp); fclose(fp); //Decode char pDecodeBuff[256 * 1024 * 2] = {0}; int iLen = DecodeBase64(pOutputBuff, iOutLen, pDecodeBuff); printf("\ndecode len %d.\n", iLen); snprintf(pic_file_name, 255, "./decode%03u.jpg", 2); FILE *fp2 = fopen(pic_file_name, "w"); fwrite(pDecodeBuff, iLen, 1, fp2); fclose(fp2);