中文编码转换——6种编码30个方向的转换
1.问题提出
在学编程序时,曾经有人问过“你可以编一个记事本程序吗?”当时很不屑一顾,但是随着学习MFC的深入,了解到记事本程序也并非易事,难点就是四种编码之间的转换。
对于编码,这是一个令初学者头疼的问题,特别是对于编码的转换,更是难以捉摸。笔者为了完成毕业设计中的一个编码转换模块,研究了中文编码和常见的字符集后,决定解决"记事本"程序的编码问题,更进一步完成GB2312、Big5、GBK、Unicode 、Unicode big endian、UTF-8共6种编码之间的任意转换。
2.问题解决
(1)编码基础知识
a.了解编码和字符集
这部分内容,我不在赘述,可参见CSDN Ancky的专栏中《各种字符集和编码详解》
博客地址:http://blog.csdn.net/ancky/article/details/2034809
b.单字节、双字节、多字节
这部分内容,可参见我先前翻译的博文《C++字符串完全指南--第一部分:win32 字符编码》
博客地址:http://blog.csdn.net/ziyuanxiazai123/article/details/7482360
c.区域和代码页
这部分内容,可参见博客 http://hi.baidu.com/tzpwater/blog/item/bd4abb0b60bff1db3ac7636a.html
d.中文编码GB2312、GBK、Big5,这部分内容请参见CSDN lengshine 博客中《GB2312、GBK、Big5汉字编码
》,博客地址:http://blog.csdn.net/lengshine/article/details/5470545
e.Windows程序的字符编码
这部分内容,可参见博客http://blog.sina.com.cn/s/blog_4e3197f20100a6z2.html 中《Windows程序的字符编码》
(2)编码总结
a.六种编码的特点
六种编码的特点如下图所示:
b.编码存储差别
ANSI(在简体中文中默认为GB2312)、Unicode、Unicode big endian 、UTF-8存储存在差别。
以中文"你好"二字为例,他们存贮格式如下图所示:
c.GB2312、Big5、GBK编码的区别
三者中汉字均采用二个字节表示,但是字节表示的值范围有所不同,如下图所示:
(3)编码转换方式
6种编码互相转换,由排列组合知识知道共有30个方向的转换.笔者采用的转换方法,
多字节文件与Unicode文件转换如下图所示:
多字节文件之间转换如下图所示:
(4)编码转换使用的三个函数
a.MultiByteToWideChar
该函数完成多字节字符串向Unicode宽字符串的转换.
函数原型为:
int MultiByteToWideChar( UINT CodePage, // 代码页 DWORD dwFlags, // 转换标志 LPCSTR lpMultiByteStr, // 待转换的字符串 int cbMultiByte, // 待转换字符串的字节数目 LPWSTR lpWideCharStr, // 转换后宽字符串的存储空间 int cchWideChar // 转换后宽字符串的存储空间大小 以宽字符大小为单位);b.WideCharToMultiByte该函数完成Unicode宽字符串到多字节字符串的转换,使用方法具体参见MSDN。以上两个函数可以完成大部分的字符串转换,可以将其封装成多字节和宽字节之间的转换函数:[cpp]view plaincopyprint?
wchar_t* Coder::MByteToWChar(UINT CodePage,LPCSTR lpcszSrcStr)
{
LPWSTR lpcwsStrDes=NULL;
int len=MultiByteToWideChar(CodePage,0,lpcszSrcStr,-1,NULL,0);
lpcwsStrDes=new wchar_t[len+1];
if(!lpcwsStrDes)
return NULL;
memset(lpcwsStrDes,0,sizeof(wchar_t)*(len+1));
len=MultiByteToWideChar(CodePage,0,lpcszSrcStr,-1,lpcwsStrDes,len);
if(len)
return lpcwsStrDes;
else
{
delete[] lpcwsStrDes;
return NULL;
}
}
char* Coder::WCharToMByte(UINT CodePage,LPCWSTR lpcwszSrcStr)
{
char* lpszDesStr=NULL;
int len=WideCharToMultiByte(CodePage,0,lpcwszSrcStr,-1,NULL,0,NULL,NULL);
lpszDesStr=new char[len+1];
memset(lpszDesStr,0,sizeof(char)*(len+1));
if(!lpszDesStr)
return NULL;
len=WideCharToMultiByte(CodePage,0,lpcwszSrcStr,-1,lpszDesStr,len,NULL,NULL);
if(len)
return lpszDesStr;
else
{
delete[] lpszDesStr;
return NULL;
}
}
c.LCMapString
依赖于本地机器的字符转换函数,尤其是中文编码在转换时要依赖于本地机器,
直接利用上述a、b中叙述的函数会产生错误,例如直接从GB2312转换到Big5,利用MultiByteToWideChar函数将GB2312转换到Unicode字符串,然后从Unicode字符串利用函数WideCharToMultiByte转换成Big5,将会发生错误,错误的结果如下图所示:
因此中文编码转换时适当使用LCMapString函数,才能完成正确的转换.例如:[cpp]view plaincopyprint?
//简体中文 GB2312 转换成 繁体中文BIG5
char* Coder::GB2312ToBIG5(const char* szGB2312Str)
{
LCID lcid = MAKELCID(MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC);
int nLength = LCMapString(lcid,LCMAP_TRADITIONAL_CHINESE,szGB2312Str,-1,NULL,0);
char* pBuffer=new char[nLength+1];
if(!pBuffer)
return NULL;
LCMapString(lcid,LCMAP_TRADITIONAL_CHINESE,szGB2312Str,-1,pBuffer,nLength);
pBuffer[nLength]=0;
wchar_t* pUnicodeBuff = MByteToWChar(CP_GB2312,pBuffer);
char* pBIG5Buff = WCharToMByte(CP_BIG5,pUnicodeBuff);
delete[] pBuffer;
delete[] pUnicodeBuff;
return pBIG5Buff;
}
(5)编码实现
实现Coder类完成编码转换工作.
Coder类的代码清单如下:
[cpp]view plaincopyprint?
// Coder.h: interface for the Coder class.
//
//
#if !defined(AFX_ENCODING_H__2AC955FB_9F8F_4871_9B77_C6C65730507F__INCLUDED_)
#define AFX_ENCODING_H__2AC955FB_9F8F_4871_9B77_C6C65730507F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//-----------------------------------------------------------------------------------------------
//程序用途:实现GB2312、big5、GBK、Unicode、Unicode big endian、UTF-8六种编码的任意装换
//程序作者:湖北师范学院计算机科学与技术学院 王定桥
//核心算法:根据不同编码特点向其他编码转换
//测试结果:在Windows7 VC6.0环境下测试通过
//制作时间:2012-04-24
//代码版权:代码公开供学习交流使用 欢迎指正错误 改善算法
//-----------------------------------------------------------------------------------------------
//Windows代码页
typedef enum CodeType
{
CP_GB2312=936,
CP_BIG5=950,
CP_GBK=0 //此处特殊处理 CP_GBK仅作一标志 GBK代码页值尚未查得
}CodePages;
//txt文件编码
typedef enum TextCodeType
{
GB2312=0,
BIG5=1,
GBK=2,
UTF8=3,
UNICODE=4,
UNICODEBIGENDIAN=5,
DefaultCodeType=-1
}TextCode;
class Coder
{
public:
Coder();
virtual ~Coder();
public:
//默认一次转换字节大小
UINT PREDEFINEDSIZE;
//指定转换时默认一次转换字节大小
void SetDefaultConvertSize(UINT nCount);
//编码类型转换为字符串
CString CodeTypeToString(TextCode tc);
//文件转到另一种