Unicode编码的使用心得及Unicode格式的INI文件
2016-03-17 13:47
543 查看
1、引言
在软件开发过程中,尤其是界面软件的开发,面向的客户不确定,客户使用的环境更加不确定,往往由于使用的编码问题,导致软件的使用产生问题。
Microsoft从Windows 2000开始,所有的WindowsAPI的接口都是Unicode格式,但是为了开发人员方便,Microsoft提供了Ansi接口,不过Ansi接口最终调用的还是Unicode的接口,只不过中间经过了一层转换罢了。所以在Microsoft平台开发,调用Unicode接口效率比调用Ansi接口效率会有所提高,中间节省了Ansi编码到Unicode编码的转换,节省了内存的申请与释放。更有利的是,使用Unicode编码,不会再由于OS系统语言的不同,产生不必要的麻烦,才能开发出真真正正的全球化软件。
2、个人开发中遇到的问题
前段时间由于开发一个软件,需要调用别人的接口,虽然我的软件是Unicode编码,对方的模块也是Unicode编码,但是对方提供的接口却是Ansi接口,在非中文系统下,由于涉及到中文路径,导致Ansi和Unicode编码转换出现错误,转换结果不可逆转。当OS的区域语言设置为中文时,转换接口可以逆转,可以正常使用。下面图一图二展示了不同的区域语言设置产生的效果。16进制大约的字符串为转换后的Ansi字符串。
图一、系统区域语言设为中文时
图二、系统区域语言设为非中文时
测试代码:
//messagebox buffer
wchar_t wc[1000] = {0};
//
char c1[] =
"新建文件夹";
wchar_t wc1[] = L"新建文件夹";
wcscat(wc,L"新建文件夹(ansi)
char = ");
for(int i = 0; i <strlen(c1); ++i)
wsprintf(wc,L"%s %0X", wc, (unsigned
char)c1[i]);
wsprintf(wc,L"%s len = %d\n", wc, strlen(c1));
//ansi->unicode->ansi
wcscat(wc,L"ansi->unicode->ansi
ansi = 新建文件夹\n");
//ansi to unicode
wchar_t *pwc1 = Ansi2WChar(c1);
wcscat(wc,L"ansi to unicode = ");
wsprintf(wc,L"%s%s len = %d\n", wc,pwc1,wcslen(pwc1));
//unicode to ansi
char *pc1 = WChar2Ansi(pwc1);
wcscat(wc,L"unicode to ansi = ");
for(int i = 0; i <strlen(pc1); ++i)
wsprintf(wc,L"%s %0X", wc, (unsigned
char)pc1[i]);
wsprintf(wc,L"%s len = %d\n", wc,strlen(pc1));
delete []pwc1;
delete []pc1;
//unicode->ansi->unicode
wcscat(wc,L"\n\nunicode->ansi->unicode
unicode = 新建文件夹\n");
//unicode to ansi
pc1= WChar2Ansi(wc1);
wcscat(wc,L"unicode to ansi = ");
for(int i = 0; i <strlen(pc1); ++i)
wsprintf(wc,L"%s %0X", wc, (unsigned
char)pc1[i]);
wsprintf(wc,L"%s len = %d\n", wc,strlen(pc1));
//ansi to unicode
wcscat(wc,L"ansi to unicode = ");
pwc1= Ansi2WChar(pc1);
wsprintf(wc,L"%s%s len = %d\n", wc,pwc1,wcslen(pwc1));
delete []pwc1;
delete []pc1;
MessageBoxW(wc);
注意:Ansi2WChar和WChar2Ansi调用的是Windows API进行转换的,代码如下:
char * WChar2Ansi(constwchar_t * pW)
{
DWORDdwNum = WideCharToMultiByte(CP_OEMCP, NULL, pW, -1, NULL, 0, NULL, FALSE);
char *pValue =
new char[dwNum + 1];
if(pValue)
{
memset(pValue,0, (dwNum + 1) *
sizeof(pValue[0]));
WideCharToMultiByte(CP_OEMCP,NULL, pW, -1, pValue, dwNum, NULL, FALSE);
}
return pValue;
}
wchar_t * Ansi2WChar(constchar * pA)
{
DWORDdwNum = MultiByteToWideChar (CP_ACP, 0, pA, -1, NULL, 0);
wchar_t *pValue =
newwchar_t[dwNum + 1];
if(pValue)
{
memset(pValue,0, (dwNum + 1) *
sizeof(pValue[0]));
MultiByteToWideChar(CP_ACP, 0, pA, -1,pValue , dwNum);
}
return pValue;
}
3、Unicode格式的INI文件
Microsoft提供了GetPrivateProfileStringA、WritePrivateProfileStringA、GetPrivateProfileStringW和WritePrivateProfileStringW用于读写INI文件;一下分成四种情况讨论字符串内部的转换逻辑
1)、文件格式为ANSI
a、调用GetPrivateProfileStringA和WritePrivateProfileStringA接口:首先转换成GetPrivateProfileStringW和WritePrivateProfileStringW接口的调用,中间经过了ANSI字符串到Unicode字符串的转换(系统完成),然后在写文件时,又将Unicode字符串转换成Ansi字符串进行存储(系统完成),中间经过了两个不必要的转换步骤;
b、调用GetPrivateProfileStringW和WritePrivateProfileStringW接口:在写文件时,将Unicode字符串转换成Ansi字符串进行存储(系统完成),中间经过了一个不必要的转换步骤。
2)、文件格式为Unicode
a、调用GetPrivateProfileStringA和WritePrivateProfileStringA接口:首先转换成GetPrivateProfileStringW和WritePrivateProfileStringW接口的调用,中间经过了ANSI字符串到Unicode字符串的转换(系统完成),在写文件时,不再需要转换,中间经过了一个个不必要的转换步骤;
b、调用GetPrivateProfileStringW和WritePrivateProfileStringW接口:由于文件格式和调用的接口都是Unicode格式,所以不存在中间转换过程,提高了效率。
4、Unicode格式的INI文件创建
由于系统默认首次创建的文件为ANSI格式,所以需要在使用该文件之前,先用Unicode格式创建好INI文件,这样在多写时就是正常的INI文件了。目前我知道创建文件有两种格式:
1)、向文件中写入Unicode的文件头信息,具体文件写入有
FILE *fp;
fp = _tfopen(_T("e:\\sss.ini"),_T("r"));
if (fp == NULL)
{
fp=_tfopen(_T("e:\\sss.ini"), _T("w+b"));
wchar_t m_strUnicode[1];
m_strUnicode[0] = wchar_t(0XFEFF);
fputwc(*m_strUnicode,fp);
}
fclose(fp);
2)、以Unicode格式创建新文件
FILE *pFile(NULL);
if((nRet= _wfopen_s(&pFile, m_wszConfigFile, L"wt, ccs=UNICODE")) == 0)
fclose(pFile);
注意:调用读写INI文件的接口其实最后都是Unicode接口,具体写入到文件中的内容是由文件的格式决定,并非调用的接口决定。
在软件开发过程中,尤其是界面软件的开发,面向的客户不确定,客户使用的环境更加不确定,往往由于使用的编码问题,导致软件的使用产生问题。
Microsoft从Windows 2000开始,所有的WindowsAPI的接口都是Unicode格式,但是为了开发人员方便,Microsoft提供了Ansi接口,不过Ansi接口最终调用的还是Unicode的接口,只不过中间经过了一层转换罢了。所以在Microsoft平台开发,调用Unicode接口效率比调用Ansi接口效率会有所提高,中间节省了Ansi编码到Unicode编码的转换,节省了内存的申请与释放。更有利的是,使用Unicode编码,不会再由于OS系统语言的不同,产生不必要的麻烦,才能开发出真真正正的全球化软件。
2、个人开发中遇到的问题
前段时间由于开发一个软件,需要调用别人的接口,虽然我的软件是Unicode编码,对方的模块也是Unicode编码,但是对方提供的接口却是Ansi接口,在非中文系统下,由于涉及到中文路径,导致Ansi和Unicode编码转换出现错误,转换结果不可逆转。当OS的区域语言设置为中文时,转换接口可以逆转,可以正常使用。下面图一图二展示了不同的区域语言设置产生的效果。16进制大约的字符串为转换后的Ansi字符串。
图一、系统区域语言设为中文时
图二、系统区域语言设为非中文时
测试代码:
//messagebox buffer
wchar_t wc[1000] = {0};
//
char c1[] =
"新建文件夹";
wchar_t wc1[] = L"新建文件夹";
wcscat(wc,L"新建文件夹(ansi)
char = ");
for(int i = 0; i <strlen(c1); ++i)
wsprintf(wc,L"%s %0X", wc, (unsigned
char)c1[i]);
wsprintf(wc,L"%s len = %d\n", wc, strlen(c1));
//ansi->unicode->ansi
wcscat(wc,L"ansi->unicode->ansi
ansi = 新建文件夹\n");
//ansi to unicode
wchar_t *pwc1 = Ansi2WChar(c1);
wcscat(wc,L"ansi to unicode = ");
wsprintf(wc,L"%s%s len = %d\n", wc,pwc1,wcslen(pwc1));
//unicode to ansi
char *pc1 = WChar2Ansi(pwc1);
wcscat(wc,L"unicode to ansi = ");
for(int i = 0; i <strlen(pc1); ++i)
wsprintf(wc,L"%s %0X", wc, (unsigned
char)pc1[i]);
wsprintf(wc,L"%s len = %d\n", wc,strlen(pc1));
delete []pwc1;
delete []pc1;
//unicode->ansi->unicode
wcscat(wc,L"\n\nunicode->ansi->unicode
unicode = 新建文件夹\n");
//unicode to ansi
pc1= WChar2Ansi(wc1);
wcscat(wc,L"unicode to ansi = ");
for(int i = 0; i <strlen(pc1); ++i)
wsprintf(wc,L"%s %0X", wc, (unsigned
char)pc1[i]);
wsprintf(wc,L"%s len = %d\n", wc,strlen(pc1));
//ansi to unicode
wcscat(wc,L"ansi to unicode = ");
pwc1= Ansi2WChar(pc1);
wsprintf(wc,L"%s%s len = %d\n", wc,pwc1,wcslen(pwc1));
delete []pwc1;
delete []pc1;
MessageBoxW(wc);
注意:Ansi2WChar和WChar2Ansi调用的是Windows API进行转换的,代码如下:
char * WChar2Ansi(constwchar_t * pW)
{
DWORDdwNum = WideCharToMultiByte(CP_OEMCP, NULL, pW, -1, NULL, 0, NULL, FALSE);
char *pValue =
new char[dwNum + 1];
if(pValue)
{
memset(pValue,0, (dwNum + 1) *
sizeof(pValue[0]));
WideCharToMultiByte(CP_OEMCP,NULL, pW, -1, pValue, dwNum, NULL, FALSE);
}
return pValue;
}
wchar_t * Ansi2WChar(constchar * pA)
{
DWORDdwNum = MultiByteToWideChar (CP_ACP, 0, pA, -1, NULL, 0);
wchar_t *pValue =
newwchar_t[dwNum + 1];
if(pValue)
{
memset(pValue,0, (dwNum + 1) *
sizeof(pValue[0]));
MultiByteToWideChar(CP_ACP, 0, pA, -1,pValue , dwNum);
}
return pValue;
}
3、Unicode格式的INI文件
Microsoft提供了GetPrivateProfileStringA、WritePrivateProfileStringA、GetPrivateProfileStringW和WritePrivateProfileStringW用于读写INI文件;一下分成四种情况讨论字符串内部的转换逻辑
1)、文件格式为ANSI
a、调用GetPrivateProfileStringA和WritePrivateProfileStringA接口:首先转换成GetPrivateProfileStringW和WritePrivateProfileStringW接口的调用,中间经过了ANSI字符串到Unicode字符串的转换(系统完成),然后在写文件时,又将Unicode字符串转换成Ansi字符串进行存储(系统完成),中间经过了两个不必要的转换步骤;
b、调用GetPrivateProfileStringW和WritePrivateProfileStringW接口:在写文件时,将Unicode字符串转换成Ansi字符串进行存储(系统完成),中间经过了一个不必要的转换步骤。
2)、文件格式为Unicode
a、调用GetPrivateProfileStringA和WritePrivateProfileStringA接口:首先转换成GetPrivateProfileStringW和WritePrivateProfileStringW接口的调用,中间经过了ANSI字符串到Unicode字符串的转换(系统完成),在写文件时,不再需要转换,中间经过了一个个不必要的转换步骤;
b、调用GetPrivateProfileStringW和WritePrivateProfileStringW接口:由于文件格式和调用的接口都是Unicode格式,所以不存在中间转换过程,提高了效率。
4、Unicode格式的INI文件创建
由于系统默认首次创建的文件为ANSI格式,所以需要在使用该文件之前,先用Unicode格式创建好INI文件,这样在多写时就是正常的INI文件了。目前我知道创建文件有两种格式:
1)、向文件中写入Unicode的文件头信息,具体文件写入有
FILE *fp;
fp = _tfopen(_T("e:\\sss.ini"),_T("r"));
if (fp == NULL)
{
fp=_tfopen(_T("e:\\sss.ini"), _T("w+b"));
wchar_t m_strUnicode[1];
m_strUnicode[0] = wchar_t(0XFEFF);
fputwc(*m_strUnicode,fp);
}
fclose(fp);
2)、以Unicode格式创建新文件
FILE *pFile(NULL);
if((nRet= _wfopen_s(&pFile, m_wszConfigFile, L"wt, ccs=UNICODE")) == 0)
fclose(pFile);
注意:调用读写INI文件的接口其实最后都是Unicode接口,具体写入到文件中的内容是由文件的格式决定,并非调用的接口决定。
相关文章推荐
- HTML5 - 使用Geolocation(地理定位)获取用户的位置
- Linux内核设计与实现 第五章
- Linux读书笔记第一周
- String字符串为空
- Lesson 4 按键
- 打包xib 以及图片资源
- GAGE Octopus系列PCI/PCIe采集卡
- 搭建高可用mongodb集群(二)—— 副本集2
- 一步一步部署Laravel项目
- iOS常用的封装方法
- Material Design (三),Snackbar的使用
- 高逼格UI-ASD(Android Support Design)
- 让jquery.tmpl支持序号${index}
- ffmpeg相关网页
- PLsql设置
- Chrome通过Native Message 扩展程序获取windows MAC地址
- hdu5643 King's Game(约瑟夫环+线段树)
- LINUX CENTOS6.5 安装 SVN 以前权限的设置
- topshelf
- 【数字图像处理】浮雕效果和倒影效果