QP(Quoted-Printable)也是MIME邮件中常用的编码方式之一。同Base64 一样,它也将输入的字符串或数据编码成全是ASCII码的可打印字符串。Quoted-Printable编码的基本方法是:输入数据在 33-60、62-126范围内的,直接输出;其它的需编码为"="加两个字节的HEX码(大写)。为保证输出行不超过规定长度,可在行尾加"=/r/n"序列作为软回车。
QP编码规则如下:
规则1:除了换行符,其它任何8位长度的字节必须被表示成一个"="号加上高四位的十六进制数
和低四位的十六进制数,十六进制数是用"0123456789ABCDEF"的ASCII字符码表示,其中"ABCDEF"都必须是大写;
规则2:当8位长度的字节值在33至60(包括33和60),62至126(包括62和126)之间时,直接用该数据对应的ASCII码来表示;
规则3:当8位长度的字节值是9和32时,用该数据对应的ASCII码(9:TAB(HT),32:SPACE)来表示,
但若是出现在行尾则必须按规则1进行转换;
规则4:对于CRLF换行符必须用"=0D=0A"来代替;
规则5:QP编码每行不得超过76个字符(不包含最后的"/r/n"两个字符),否则必须换行,
换行方法是:在结尾处加上"=/r/n"
其它:符号"-"不用进行QP编码,所以不要用"-"符号来表示实体之间的分界,
最好用"=_"来代替,对于"!"#$[/]^'{|}~"这些字符必须按规则1进行转换.
参考代码如下(C++代码)
#include <string.h>
#include <stdio.h>
#include <string>
using namespace std;
// 字符转换为Hex
string Char2Hex(unsigned char cVal)
{
string strTemp;
unsigned char cTempBuf1[10] = {0};
unsigned char cTempBuf2[10] = {0};
sprintf_s((char*)cTempBuf1, 10 * sizeof(unsigned char), "%02X", cVal);
cTempBuf2[0] = toupper(cTempBuf1[0]);
cTempBuf2[1] = toupper(cTempBuf1[1]);
cTempBuf2[2] = '/0';
strTemp = (char*)cTempBuf2;
return strTemp;
}
// QP编码
bool QPEnCoding(string& aStr, bool &IsChanged)
{
IsChanged = false;
int length = (int)aStr.size();//.length();
if (length <= 0)
{
return false;
}
unsigned char *c_copy = new unsigned char[length +1];
strcpy_s((char*)c_copy, (length +1) * sizeof(unsigned char), aStr.c_str());
aStr.clear();
int nLineLen = 0;
for (int i = 0; i < length; i++)
{
// c_copy[i] 介于 33 到 126 之间, 且c_copy[i]的值不为'='的时候, 直接输出
if ((c_copy[i] >= '!') && (c_copy[i] <= '~') && (c_copy[i] != '='))
{
aStr += (char)c_copy[i];
nLineLen ++;
}
// 其它的需编码为'='加两个字节的HEX码(大写)
else
{
aStr += "=";
aStr += Char2Hex(c_copy[i]);
nLineLen += 3;
IsChanged = true;
}
// 保证输出行不超过规定长度, 可在行尾加"=/r/n"序列作为软回车
if (nLineLen >= 73)
{
aStr += "=/r/n";
nLineLen = 0;
}
}
// 释放内存
delete[] c_copy;
c_copy = NULL;
return true;
}
// QP解码
bool QPDeCoding(string& aStr, bool &IsChanged)
{
// 输出的字符计数
IsChanged = false;
int nSrcLen = (int)aStr.size();
if (nSrcLen <= 0)
{
return false;
}
unsigned char* pcTemp1 = 0;
unsigned char* pDst = new unsigned char[nSrcLen +1];
memset(pDst, 0, nSrcLen +1);
pcTemp1 = pDst;
char* pcTemp2 = 0;
char* pSrc = new char[nSrcLen +1];
strcpy_s((char*)pSrc, (nSrcLen + 1) * sizeof(char), aStr.c_str());
aStr.clear();
pcTemp2 = pSrc;
int i = 0;
while (i < nSrcLen)
{
// 软回车,跳过
if (strncmp(pSrc, "=/r/n", 3) == 0)
{
pSrc += 3;
i += 3;
}
else
{
// 是编码字节
if (*pSrc == '=')
{
sscanf_s(pSrc, "=%02X", pDst++);
pSrc += 3;
i += 3;
IsChanged = true;
}
// 非编码字节
else
{
*pDst++ = (unsigned char)*pSrc++;
i ++;
}
}
}
// 输出加个结束符
pDst = '/0';
aStr = (char*)pcTemp1;
delete[] pcTemp1;
pcTemp1 = 0;
delete[] pcTemp2;
pcTemp2 = 0;
return true;
}