遇到一个问题:要将一个指定长度的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);
浙公网安备 33010602011771号