如若不是巴比伦人修筑了通天塔,妄图一窥上帝的居所,上帝也许不会一怒之下将世人全都分散开来,拥有不同的肤色,使用不同的语言,当然也就完全没有了文字的差异,也就没有了这篇关于字符串编码和解码的日记。
int URLEncode(const char* url, char* buffer, int length)
{
int nRet = 0;
// create the temp pointer.
unsigned char* pInBuf = (unsigned char*)url;
unsigned char* pOutBuf = (unsigned char*)buffer;
while (*pInBuf) {
if (isalnum(*pInBuf)) { // change the number and alpha
if (length != 0) {
if (length > nRet) {
*pOutBuf++ = *pInBuf;
} else {
nRet = 0;
break;
}
}
++nRet;
} else { // change the no alpha character
if (isspace(*pInBuf)) {
if (length != 0) {
if (length > nRet) {
*pOutBuf++ = '+';
} else {
nRet = 0;
break;
}
}
++nRet;
} else {
if (length != 0) {
if (length > nRet) {
*pOutBuf++ = '%';
*pOutBuf++ = TOHEX(*pInBuf >> 4);
*pOutBuf++ = TOHEX(*pInBuf & 0x0F);
} else {
nRet = 0;
break;
}
}
nRet += 3;
}
}
pInBuf++;
}
if (length != 0) {
if (length > nRet) {
*pOutBuf = '\0';
} else {
nRet = 0;
}
}
return(nRet ¡ nRet + 1 : 0);
}
首先看上一个函数,它是一个将URL编码的函数,将ANSI字符集字符转换成网络上用的URL编码,在很多网络协议里(比如MSN的协议),HTTPS服务返回的字符串都是经过如此编码的,同样,要向这些服务器发送请求,自然就需要编码。编码的规范是:空格用+代替,英文字母不变,除过英文字母的所有字符使用%加其UTF8编码表示。
下面是解码函数,是一个逆向工程。
int URLDecode(const char* url, char* buffer, int length)
{
int nRet = 0;
// create the temp pointer.
unsigned char* pInBuf = (unsigned char*)url;
unsigned char* pOutBuf = (unsigned char*)buffer;
while (*pInBuf) {
if (*pInBuf == '+') {
if (length != 0) {
if (length > nRet) {
*pOutBuf++ = ' ';
} else {
nRet = 0;
break;
}
}
} else if (*pInBuf == '%') {
if (length != 0) {
if (length > nRet) {
*pOutBuf++ = MAKEBYTE(TODEC(*(pInBuf + 2)), TODEC(*(pInBuf + 1)));
} else {
nRet = 0;
break;
}
}
pInBuf += 2;
} else {
if (length != 0) {
if (length > nRet) {
*pOutBuf++ = *pInBuf;
} else {
nRet = 0;
break;
}
}
}
++pInBuf;
++nRet;
}
if (length != 0) {
if (length > nRet) {
*pOutBuf = '\0';
} else {
nRet = 0;
}
}
return(nRet ¡ nRet + 1 : 0);
}
上面两个函数如若将参数length设置为0,函数返回所需分配的缓冲区长度。
上述函数中使用的宏定义如下:
#define MAKEBYTE(low, high) (((high) << 4) | (low))
#define TOHEX(x) ((x) > 9 ?(x) + 55 : (x) + 48)
#define TODEC(x) (((x) - 55 > 9) ? (x) - 55 : (x) - 48)
除了上述编码外,文字编码不外乎就是互转了。一般的简便方法就是利用本地语言函数将ANSI编码的东西转为Unicode编码,然后在将Unicode编码转为其它编码。Unicode是一个标准,任何一种转码器只要和Unicode可以互操作就可以了。
在Unicode出现以前,很多地区都使用自己定义的编码,比如中国的GBK码和台湾的Big5码,它们其实就是使用了两个ANSI编码的长度来编码一位信息的,因为ANSI编码只使用了7bit,刚好两个ANSI编码一共空出来了2bit以示区别。Unicode的出现彻底改变了一切,Unicode的原理和双字节编码差不多,但是它的标准可以适合世界上任何一种语言。很可惜,GBK编码和Unicode不兼容,无法进行算术转换,只能使用查表转换。
const char* Lan_Unicode2Ansi(HANDLE hLan, const wchar_t* unicode)
{
ASSERT(hLan && unicode);
LPLAN_ALLOC pLan = (LPLAN_ALLOC)hLan;
pLan->lasterr = 0;
const char* pRet = NULL;
int nRet = WideCharToMultiByte(CP_ACP, 0, unicode, -1, NULL, 0, NULL, NULL);
if (nRet > 0) {
if (ResizeBuffer(hLan, nRet, lan_ansi)) {
nRet = WideCharToMultiByte(CP_ACP, 0, unicode, -1, pLan->ansi_buffer, nRet, NULL, NULL);
if (nRet > 0) {
pRet = pLan->ansi_buffer;
} else {
pLan->lasterr = GetLastError();
}
}
} else {
pLan->lasterr = GetLastError();
}
return(pRet);
}
上述函数将Unicode转换成了ANSI,使用的语言代码页是微软定义的CP_ACP,即:ANSI code page。第一次调用WideCharToMultiByte得到缓冲区长度,再次调用进行实质性的编码转换。
const char* Lan_Ansi2UTF8(HANDLE hLan, const char* ansi)
{
ASSERT(hLan && ansi);
LPLAN_ALLOC pLan = (LPLAN_ALLOC)hLan;
pLan->lasterr = NULL;
const char* pRet = NULL;
int nRet = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0);
if (nRet) {
if (ResizeBuffer(hLan, nRet, lan_unicode)) {
nRet = MultiByteToWideChar(CP_ACP, 0, ansi, -1, pLan->unicode_buffer, nRet);
if (nRet > 0) {
nRet = WideCharToMultiByte(CP_UTF8, 0, pLan->unicode_buffer, -1, NULL, 0, NULL, NULL);
if (nRet > 0) {
if (ResizeBuffer(hLan, nRet, lan_ansi)) {
nRet = WideCharToMultiByte(CP_UTF8, 0, pLan->unicode_buffer, -1, pLan->ansi_buffer, nRet, NULL, NULL);
if (nRet > 0) {
pRet = pLan->ansi_buffer;
}
}
} else {
pLan->lasterr = GetLastError();
}
} else {
pLan->lasterr = GetLastError();
}
}
} else {
pLan->lasterr = GetLastError();
}
return(pRet);
}
这里可以看出一个操作系统对于编码转换的支持程度,Windows2000以后的操作系统是Unicode编码为核心的,这样任何一个函数都等于在讲普通话,底层函数之间没有沟通困难,而微软操作系统提供的接口函数在字符编码转换上给与程序员极大的帮助。
const char* Lan_Ansi2UTF8(HANDLE hLan, const char* ansi)
{
ASSERT(hLan && ansi);
LPLAN_ALLOC pLan = (LPLAN_ALLOC)hLan;
pLan->lasterr = NULL;
const char* pRet = NULL;
int nRet = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0);
if (nRet) {
if (ResizeBuffer(hLan, nRet, lan_unicode)) {
nRet = MultiByteToWideChar(CP_ACP, 0, ansi, -1, pLan->unicode_buffer, nRet);
if (nRet > 0) {
nRet = WideCharToMultiByte(CP_UTF8, 0, pLan->unicode_buffer, -1, NULL, 0, NULL, NULL);
if (nRet > 0) {
if (ResizeBuffer(hLan, nRet, lan_ansi)) {
nRet = WideCharToMultiByte(CP_UTF8, 0, pLan->unicode_buffer, -1, pLan->ansi_buffer, nRet, NULL, NULL);
if (nRet > 0) {
pRet = pLan->ansi_buffer;
}
}
} else {
pLan->lasterr = GetLastError();
}
} else {
pLan->lasterr = GetLastError();
}
}
} else {
pLan->lasterr = GetLastError();
}
return(pRet);
}
Unicode的优点在于信息码定长,操作简单,运行速度快,当然它的缺点是比较占用内存,对于英语言为主的国家,这种浪费是不必要的,所以压缩编码UTF-8就被提出来了,UTF-8使用首位作为判断位,信息位7位到23位不等。利用微软接口函数,也可以方便的转换。
const char* Lan_UTF82Ansi(HANDLE hLan, const char* utf8)
{
ASSERT(hLan && utf8);
LPLAN_ALLOC pLan = (LPLAN_ALLOC)hLan;
pLan->lasterr = 0;
const char* pRet = NULL;
int nRet = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
if (nRet > 0) {
if (ResizeBuffer(hLan, nRet, lan_unicode)) {
nRet = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, pLan->unicode_buffer, nRet);
if (nRet > 0) {
nRet = WideCharToMultiByte(CP_ACP, 0, pLan->unicode_buffer, -1, NULL, 0, NULL, NULL);
if (nRet > 0) {
if (ResizeBuffer(hLan, nRet, lan_ansi)) {
nRet = WideCharToMultiByte(CP_ACP, 0, pLan->unicode_buffer, -1, pLan->ansi_buffer, nRet, NULL, NULL);
if (nRet > 0) {
pRet = pLan->ansi_buffer;
}
}
} else {
pLan->lasterr = GetLastError();
}
} else {
pLan->lasterr = GetLastError();
}
}
} else {
pLan->lasterr = GetLastError();
}
return(pRet);
}
当然,离开了操作系统,应用程序也就没有意义了,所以复杂的查表法转换就不用熟悉它了,毕竟没有什么大用处。操作系统都内置有转码函数,用就是了。
程序中用到的数据结构为:
typedef struct LAN_ALLOC
{
int size_unicode;
int size_ansi;
char* ansi_buffer;
wchar_t* unicode_buffer;
int lasterr;
} FAR * LPLAN_ALLOC;
定义这个结构的目的就是为了尽可能的减少对堆的操作,毕竟堆内存的分配很浪费时间,有了这个结构以后,堆得占用会在一段时间后达到一个稳定值,可以大幅度减少分配释放操作。
配套函数如下:
enum mem_type {lan_unicode = 0x01, lan_ansi = 0x02};
HANDLE Lan_CreateHandle()
{
LPLAN_ALLOC pLan = new LAN_ALLOC;
if (pLan) {
pLan->unicode_buffer = NULL;
pLan->ansi_buffer = NULL;
pLan->size_unicode = 0;
pLan->size_ansi = 0;
pLan->lasterr = 0;
}
return((HANDLE)pLan);
}
BOOL ResizeBuffer(HANDLE hLan, size_t size, int mem)
{
ASSERT(hLan);
LPLAN_ALLOC pLan = (LPLAN_ALLOC)hLan;
pLan->lasterr = 0;
if (size == 0) {
if (mem & lan_unicode) {
if (pLan->unicode_buffer) {
delete[](pLan->unicode_buffer);
}
pLan->unicode_buffer = NULL;
pLan->size_unicode = 0;
}
if (mem & lan_ansi) {
if (pLan->ansi_buffer) {
delete[](pLan->ansi_buffer);
}
pLan->ansi_buffer = NULL;
pLan->size_ansi = 0;
}
} else {
if (mem & lan_unicode) {
if (pLan->size_unicode < (int)size) {
wchar_t* pNewAlloc = new wchar_t[size];
if (pNewAlloc) {
pLan->size_unicode = (int)size;
if (pLan->unicode_buffer) {
delete[](pLan->unicode_buffer);
}
} else {
pLan->lasterr = ERROR_OUTOFMEMORY;
}
pLan->unicode_buffer = pNewAlloc;
}
}
if (mem & lan_ansi) {
if (pLan->size_ansi < (int)size) {
char* pNewAlloc = new char[size];
if (pNewAlloc) {
pLan->size_ansi = (int)size;
if (pLan->ansi_buffer) {
delete[](pLan->ansi_buffer);
}
} else {
pLan->lasterr = ERROR_OUTOFMEMORY;
}
pLan->ansi_buffer = pNewAlloc;
}
}
}
return(pLan->lasterr == 0);
}
int GetBufferLength(HANDLE hLan, int mem)
{
ASSERT(hLan);
LPLAN_ALLOC pLan = (LPLAN_ALLOC)hLan;
int nRet;
if (mem == lan_unicode) {
nRet = pLan->size_unicode;
} else {
nRet = pLan->size_ansi;
}
return(nRet);
}
void Lan_CloseHandle(HANDLE hLan)
{
ASSERT(hLan);
LPLAN_ALLOC pLan = (LPLAN_ALLOC)hLan;
if (pLan) {
if (pLan->ansi_buffer) {
delete[](pLan->ansi_buffer);
}
if (pLan->unicode_buffer) {
delete[](pLan->unicode_buffer);
}
delete(pLan);
}
}
浙公网安备 33010602011771号