使你的C/C++代码支持Unicode

本文摘自I18nGuy主页的一篇内容,原文地址:http://www.i18nguy.com/unicode/c-unicode.zh-CN.html  

文档在线看:http://www.docin.com/p-37841375.html

 

这份文档简要的说明了如何修改你的C/C++代码使之支持Unicode。在这里并不准备解释太多相关的技术细节并且我得假定你已经基本熟悉Microsoft支持Unicode的方式。它的主要目的是方便你查询相关的数据类型和函数,以及修正相应的拼写错误。

内容
第一步
I/O, 数据库

流式 I/O
BOM 值
常量和全局变量

数据类型
Platform SDK字符串处理API
CRT字符串处理API

 

使你的C/C++代码支持Unicode的第一步

  • 定义宏 _UNICODE 如果定义了宏 _MBCS 则取消它的定义(undefine)。
  • 在字符串前添加 L 标记或者用 _T宏修饰字符串。
  • 使用 Wide 或者 TCHAR 版本的字符串处理函数。
  • 确定API中的字符串长度是按字节计数还是按字符个数计数。因为基于字符的显示和打印(与此不同的是,GUI是基于像素的)使用列数,而不是字节数或者字符个数。
  • 在字符串指针相关的计算中使用GetNext格式,因为一个字符可能包含多于一个Unicode字符单元。
  • 注意缓冲区的大小以及防止缓冲区溢出。
    改变编码方式可能需要增大缓冲区或者限制字符串的最大长度。假设单个字符的大小从1个字节变为4个字节,并且字符串本来20个字符占用20字节,那么你需要将字符串缓冲区扩大为80字节或者将字符串长度限制为5个字符(字符串缓 冲区仍为20字节)。注意缓冲区的扩大可能被限制到一个最大值(比如65KB)。减少字符串长度到一个固定值可能破坏现有的程序,限制字符串长度到固定 值可能是危险的。比如,限制到20字节,将字符串转化为大写形式就可能导致字符串变长并且超过限制。
  • 将接受或者返回单字符参数的函数替换为使用字符串的版本。 (在一些语言中) 对于单个字符的操作可能导致返回多个代码点。例如,upper('ß')将返回"SS"
  • 使用 wmain 代替 main。环境变量也由_environ变为_wenviron
    wmain(int argc, wchar_t *argv[], wchar_t *envp[])
  • MFC Unicode 程序使用  wWinMain 作为程序入口点(VC++ 6.0)。
    project->settings->Link,选择output选项卡
    在Entry point symbol一行加上 wWinMainCRTStartup
  • 字体的因素,分清使用的字体用于渲染各种语言的字符还是用于脚本中。

文件 I/O, 数据库, 传输协议等因素

  • 考虑是否需要读写文件、数据库中的 UTF-8 或者 UTF-16 字符,以及是否进行数据交换。
  • 考虑 UTF-16 格式文件中的字节序。
    读写网络传输的数据总是使用 Big-Endian ,如果你没有产生 BOM 也使用 Big-Endian。
    文件的字节序依赖于文件格式以及/或者源/目标机器的体系结构。
    读取 UTF-16 或者 UTF-32编码的文件时,考虑是否需要将字符按字节逆序。
    对于 streams 和传输协议也需要做上述的考虑。
  • 传输协议和用于数据交换的文件要使用正确的编码方式。例如 HTTP,HTML,XML 必须设置为 UTF-8 或者 UTF-16。
  • 考虑Unicode字节序标记( BOM ,Byte Order Marker) 以及是否需要将它同数据一同写入。读取数据时记得去掉BOM。
  • 考虑遗留数据和文件的编码惯例,考虑导入和导出以及传输协议。(MultiByteToWideChar, WideCharToMultiByte, mbtowc, wctomb, wctombs, mbstowcs )
  • 考虑复制文本到剪贴板
    使用 CF_TEXT 格式并且写入本地编码的文本(ANSI格式)
    使用 CF_UNICODETEXT 格式并写入Unicode文本
  • 数据库程序需要考虑数据类型 (NCHAR, NVARCHAR) 和模式(schema)的变更,触发器,存储过程和查询,数据的增长,索引以及性能等因素。
    注意:Unicode模式的改变对于不同厂商的数据库产生不同的影响。如果需要数据库具有较好的可移植性,就要考虑到 每种数据库的特性和行为。
    (我知道针对这个问题说得不够多,以后有机会再补充吧)

流式 I/O

如果你使用Microsoft C++编译器,你可能遇到与流式 I/O相关的3类问题:

  1. 不支持Unicode文件名。
    解决方式是使用 FILE * _wfopen 函数,之后使用FILE句柄初始化流式 I/O。
    std::ifstream stm(_wfopen(pFilename, L"r"));
  2. 在 读/写 的时候,流式 I/O 会把数据从本地代码页(ANSI格式)转换到Unicode格式/从Unicode格式转换到ANSI格式,而非UTF-8 或者 UTF-16。
    但是可以修改表示流的类使之支持读写 UTF-8 格式字符。你可以自己实现一个读写时把数据在 Unicode 和 UTF-8 之间转换的I/O stream类。
    codecvt <wchar_t, char_traits <wchar_t> >
  3. 如果要用流式 I/O读写 UTF-16 字符,应该用二进制模式打开并且在二进制模式下输入输出。可以用如下方法设 置 I/O 为二进制模式:
    _setmode( _fileno( stdin ), _O_BINARY );

    也可以参考 Microsoft 运行时库参考: "Unicode Stream I/O in Text and Binary Modes".

注意:针对cout/wcout, cin/wcin等并没有相应的 TCHAR 版本。如果你需要在ANSI/Unicode两种模式下编译代码,你可能需要自己定义一个名字类似"tout"的宏。

国际化,Unicode高级技术,平台和其它因素

  • 考虑使用基于地区的办法和更进一步的国际化。
  • 对于 Windows 95,98 和 Windiws ME,考虑使用 Microsoft MSLU (Microsoft Layer for Unicode)
  • 考虑字符串比较和排序,Unicode Collation Algorithm
  • 考虑 Unicode Normalization
  • 考虑 Character Folding
  • 重新考虑是否要自己处理这些事情。借助于一家Unicode咨询公司 ,然后让你的团队集中精力做他们擅长的事情。(嗨,我们也要谋生啊...)

Unicode字节序标记(BOM) 值

编码方式BOM值
UTF-8EF BB BF
UTF-16
(big-endian)
FE FF
UTF-16
(little-endian)
FF FE
UTF-16BE, UTF-32BE
(big-endian)
No BOM!
UTF-16LE, UTF-32LE
(little-endian)
No BOM!
UTF-32
(big-endian)
00 00 FE FF
UTF-32
(little-endian)
FF FE 00 00
SCSU
(compression)
0E FE FF

 

U +FEFF。(它也能表示一个被称作 Zero Width No-break Space 的字符)。U+FFFE 这个代码点在Unicode中是非法的,它永远不应该出现在一个Unicode字符流中。所以BOM可以作为放置于文件(或者一 个字符串)的起始作为字节序的指示器。对UTF-16编码而言,如果第一个字符的值是FE FF 那么文本和读取文本的机器有相同的字节序。如果是 FF FE,那么有相反的字节序并且需要对每个16-bit字按字节逆序。同样的,BOM指示了UTF-32编码的文本的字节序。

注意不是所有的文件都以Unicode字节序标记开始。事实上,Unicode标准称若不以Unicode字节序标记 (BOM)开始(数据)就必须被表示成big-endian形式。

字符 U+FEFF 同样作为不同Unicode编码方式的标记。左边的表格说明了 U+FEFF 在每一种Unicode编码方式中的值。注意:按照定义,标记为UTF-16BE, UTF-32BE, UTF-32LE or UTF-16LE 的文本不应该有BOM,字节序已经由标记本身指出了。

对于使用 SCSU

(Standard Compression Scheme for Unicode) 算法压缩过的文本,也有一个推荐的签名。

 

常量和全局变量

ANSI版本宽字符版本宏定义版本(TCHAR)
EOFWEOF_TEOF
_environ_wenviron_tenviron
_pgmptr_wpgmptr_tpgmptr

数据类型

ANSI版本宽字符版本宏定义版本(TCHAR)
charwchar_t_TCHAR
_finddata_t_wfinddata_t_tfinddata_t
__finddata64_t__wfinddata64_t_tfinddata64_t
_finddatai64_t_wfinddatai64_t_tfinddatai64_t
intwint_t_TINT
signed charwchar_t_TSCHAR
unsigned charwchar_t_TUCHAR
charwchar_t_TXCHAR
 L_T 或者 _TEXT
LPSTR
(char *)
LPWSTR
(wchar_t *)
LPTSTR
(_TCHAR *)
LPCSTR
(const char *)
LPCWSTR
(const wchar_t *)
LPCTSTR
(const _TCHAR *)
LPOLESTR
(For OLE)
LPWSTRLPTSTR

Platform SDK字符串处理API

有很多Windows API函数会根据宏 UNICODE 是否被定义而编译成不同形式。那些需要同时操作ANSI字符和宽字符的模块需要了解这一点。否则,应该使用宏定义版 本的名字,这样的话就只需要定义宏 UNICODE 并且重新编译程序。

下列列表并没有列举所有的有ANSI和宽字符两个版本的API,只列举了与字符和字符串处理相关的一些。如果需要 查看与代码页和地区相关的API请查看WinNLS.h头文件。

ANSI版本宽字符版本宏定义版本(TCHAR)
CharLowerACharLowerWCharLower
CharLowerBuffACharLowerBuffWCharLowerBuff
CharNextACharNextWCharNext
CharNextExACharNextExWCharNextEx
CharPrevACharPrevWCharPrev
CharPrevExACharPrevExWCharPrevEx
CharToOemACharToOemWCharToOem
CharToOemBuffACharToOemBuffWCharToOemBuff
CharUpperACharUpperWCharUpper
CharUpperBuffACharUpperBuffWCharUpperBuff
CompareStringACompareStringWCompareString
FoldStringAFoldStringWFoldString
GetStringTypeAGetStringTypeWGetStringType
GetStringTypeExAGetStringTypeExWGetStringTypeEx
IsCharAlphaAIsCharAlphaWIsCharAlpha
IsCharAlphaNumericAIsCharAlphaNumericWIsCharAlphaNumeric
IsCharLowerAIsCharLowerWIsCharLower
IsCharUpperAIsCharUpperWIsCharUpper
LoadStringALoadStringWLoadString
lstrcatAlstrcatWlstrcat
lstrcmpAlstrcmpWlstrcmp
lstrcmpiAlstrcmpiWlstrcmpi
lstrcpyAlstrcpyWlstrcpy
lstrcpynAlstrcpynWlstrcpyn
lstrlenAlstrlenWlstrlen
OemToCharAOemToCharWOemToChar
OemToCharBuffAOemToCharBuffWOemToCharBuff
wsprintfAwsprintfWwsprintf
wvsprintfAwvsprintfWwvsprintf

CRT字符串处理API

函数按照ANSI版本的ASCII字母顺序排序,方便转换到相应的Unicode版本。

ANSI版本宽字符版本宏定义版本(TCHAR)
_access_waccess_taccess
_atoi64_wtoi64_tstoi64
_atoi64_wtoi64_ttoi64
_cgets_cgetwscgetts
_chdir_wchdir_tchdir
_chmod_wchmod_tchmod
_cprintf_cwprintf_tcprintf
_cputs_cputws_cputts
_creat_wcreat_tcreat
_cscanf_cwscanf_tcscanf
_ctime64_wctime64_tctime64
_execl_wexecl_texecl
_execle_wexecle_texecle
_execlp_wexeclp_texeclp
_execlpe_wexeclpe_texeclpe
_execv_wexecv_texecv
_execve_wexecve_texecve
_execvp_wexecvp_texecvp
_execvpe_wexecvpe_texecvpe
_fdopen_wfdopen_tfdopen
_fgetchar_fgetwchar_fgettchar
_findfirst_wfindfirst_tfindfirst
_findnext64_wfindnext64_tfindnext64
_findnext_wfindnext_tfindnext
_findnexti64_wfindnexti64_tfindnexti64
_fputchar_fputwchar_fputtchar
_fsopen_wfsopen_tfsopen
_fullpath_wfullpath_tfullpath
_getch_getwch_gettch
_getche_getwche_gettche
_getcwd_wgetcwd_tgetcwd
_getdcwd_wgetdcwd_tgetdcwd
_ltoa_ltow_ltot
_makepath_wmakepath_tmakepath
_mkdir_wmkdir_tmkdir
_mktemp_wmktemp_tmktemp
_open_wopen_topen
_popen_wpopen_tpopen
_putch_putwch_puttch
_putenv_wputenv_tputenv
_rmdir_wrmdir_trmdir
_scprintf_scwprintf_sctprintf
_searchenv_wsearchenv_tsearchenv
_snprintf_snwprintf_sntprintf
_snscanf_snwscanf_sntscanf
_sopen_wsopen_tsopen
_spawnl_wspawnl_tspawnl
_spawnle_wspawnle_tspawnle
_spawnlp_wspawnlp_tspawnlp
_spawnlpe_wspawnlpe_tspawnlpe
_spawnv_wspawnv_tspawnv
_spawnve_wspawnve_tspawnve
_spawnvp_wspawnvp_tspawnvp
_spawnvpe_wspawnvpe_tspawnvpe
_splitpath_wsplitpath_tsplitpath
_stat64_wstat64_tstat64
_stat_wstat_tstat
_stati64_wstati64_tstati64
_strdate_wstrdate_tstrdate
_strdec_wcsdec_tcsdec
_strdup_wcsdup_tcsdup
_stricmp_wcsicmp_tcsicmp
_stricoll_wcsicoll_tcsicoll
_strinc_wcsinc_tcsinc
_strlwr_wcslwr_tcslwr
_strncnt_wcsncnt_tcsnbcnt
_strncnt_wcsncnt_tcsnccnt
_strncnt_wcsncnt_tcsnccnt
_strncoll_wcsncoll_tcsnccoll
_strnextc_wcsnextc_tcsnextc
_strnicmp_wcsnicmp_tcsncicmp
_strnicmp_wcsnicmp_tcsnicmp
_strnicoll_wcsnicoll_tcsncicoll
_strnicoll_wcsnicoll_tcsnicoll
_strninc_wcsninc_tcsninc
_strnset_wcsnset_tcsncset
_strnset_wcsnset_tcsnset
_strrev_wcsrev_tcsrev
_strset_wcsset_tcsset
_strspnp_wcsspnp_tcsspnp
_strtime_wstrtime_tstrtime
_strtoi64_wcstoi64_tcstoi64
_strtoui64_wcstoui64_tcstoui64
_strupr_wcsupr_tcsupr
_tempnam_wtempnam_ttempnam
_ui64toa_ui64tow_ui64tot
_ultoa_ultow_ultot
_ungetch_ungetwch_ungettch
_unlink_wunlink_tunlink
_utime64_wutime64_tutime64
_utime_wutime_tutime
_vscprintf_vscwprintf_vsctprintf
_vsnprintf_vsnwprintf_vsntprintf
asctime_wasctime_tasctime
atof_wtof_tstof
atoi_wtoi_tstoi
atoi_wtoi_ttoi
atol_wtol_tstol
atol_wtol_ttol
字符比较映射为宏或者inline 函数_tccmp
字符拷贝映射为宏或者inline 函数_tccpy
字符长度映射为宏或者inline 函数_tclen
ctime_wctime_tctime
fgetcfgetwc_fgettc
fgetsfgetws_fgetts
fopen_wfopen_tfopen
fprintffwprintf_ftprintf
fputcfputwc_fputtc
fputsfputws_fputts
freopen_wfreopen_tfreopen
fscanffwscanf_ftscanf
getcgetwc_gettc
getchargetwchar_gettchar
getenv_wgetenv_tgetenv
getsgetws_getts
isalnumiswalnum_istalnum
isalphaiswalpha_istalpha
isasciiiswascii_istascii
iscntrliswcntrl_istcntrl
isdigitiswdigit_istdigit
isgraphiswgraph_istgraph
islead (总是返回FALSE)(总是返回FALSE)_istlead
isleadbyte (总是返回FALSE)isleadbyte (总是返回FALSE)_istleadbyte
islegal (总是返回TRUE)(总是返回TRUE)_istlegal
isloweriswlower_istlower
isprintiswprint_istprint
ispunctiswpunct_istpunct
isspaceiswspace_istspace
isupperiswupper_istupper
isxdigitiswxdigit_istxdigit
mainwmain_tmain
perror_wperror_tperror
printfwprintf_tprintf
putcputwc_puttc
putcharputwchar_puttchar
puts_putws_putts
remove_wremove_tremove
rename_wrename_trename
scanfwscanf_tscanf
setlocale_wsetlocale_tsetlocale
sprintfswprintf_stprintf
sscanfswscanf_stscanf
strcatwcscat_tcscat
strchrwcschr_tcschr
strcmpwcscmp_tcscmp
strcollwcscoll_tcscoll
strcpywcscpy_tcscpy
strcspnwcscspn_tcscspn
strerror_wcserror_tcserror
strftimewcsftime_tcsftime
strlenwcslen_tcsclen
strlenwcslen_tcslen
strncatwcsncat_tcsncat
strncatwcsncat_tcsnccat
strncmpwcsncmp_tcsnccmp
strncmpwcsncmp_tcsncmp
strncpywcsncpy_tcsnccpy
strncpywcsncpy_tcsncpy
strpbrkwcspbrk_tcspbrk
strrchrwcsrchr_tcsrchr
strspnwcsspn_tcsspn
strstrwcsstr_tcsstr
strtodwcstod_tcstod
strtokwcstok_tcstok
strtolwcstol_tcstol
strtoulwcstoul_tcstoul
strxfrmwcsxfrm_tcsxfrm
system_wsystem_tsystem
tmpnam_wtmpnam_ttmpnam
tolowertowlower_totlower
touppertowupper_totupper
ungetcungetwc_ungettc
vfprintfvfwprintf_vftprintf
vprintfvwprintf_vtprintf
vsprintfvswprintf_vstprintf
WinMainwWinMain_tWinMain
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值