[其他] 字符和编码相关(UNICODE/ANSI/UTF-8)

UNICODE:

由多家大公司共同制定的一套字符集,用4字节(最初是2字节)表示一个字符,所谓的字符就是“独立的符号”,比如每个汉字都是一个字符,每个英文字母也是一个字符,每个日语片甲也是一个字符,最早的2字节(16位)最多能表示65000个字符,这65000个字符用来表示全世界所有语言的字符明显是不够用的,单单汉字就有超过十万个,而这65000个空间的分配原则如下:

16位代码区间语言文字
0 0 0 0 - 0 0 7 FASCII
0 0 8 0 - 0 0 F F拉丁文1字符
0 1 0 0 - 0 1 7 F欧洲拉丁文
0 1 8 0 - 0 1 F F扩充拉丁文
0 2 5 0 - 0 2 A F标准拼音
0 2 B 0 - 0 2 F F修改型字母
0 3 0 0 - 0 3 6 F通用区分标志
0 4 0 0 - 0 4 F F西里尔字母
0 5 3 0 - 0 5 8 F亚美尼亚文
0 5 9 0 - 0 5 F F西伯莱文
0 6 0 0 - 0 6 F F阿拉伯文
0 9 0 0 - 0 9 7 F梵文

上表中仅仅给了标准拼音的代码区间,不知道是不是没打算把汉字弄进去,这个知道的朋友可以留言给我普及一下。

后来32位的unicode的方案中,确确实实是给汉字留出了足够的空间:

3400-4DBF:CJK 统一表意符号 (CJK Unified Ideographs)

4E00-9FFF :CJK 统一表意符号 (CJK Unified Ideographs)

20000-3FFFF:CJK 统一表意符号 (CJK Unified Ideographs)

上面三段都是留给汉字的,共计157000+,即可以用unicode编码表示的汉字多达 近16万个 其他语言符号的代码区间见 百度百科 中的“编码范围”章节。

 

ANSI:

最初的单字节字符集,用单个字节表示字符,最多能表示256个字符,ANSI在不同的操作系统上会表示成不同的字符集,这是由操作系统自行定义的。需要注意 ANSI 和 ASCII 码不是同一个东西,ASCII 码是全世界统一的编码规则,每个值唯一表示一个特定字符,且在全世界范围内任何一台计算机上都不会变。

这里我们需要知道 UNICODE 是用来扩展 ANSI 容量不足通用性不够 的问题 。

 

UTF-8:

UTF-8是针对Unicode的一种压缩编码,它不是新的字符集,而是一种压缩编码,因为Unicode要求每个字符都由4个字节来表示,那么像ASCII 码这种字符就显得有些浪费,因为 ASCII 码只需要一个字节就足够了,所有UTF-8 就是用来对 Unicode进行压缩的,我们可以理解为,UTF-8如果想直接在屏幕上显示的话,还是需要先把 UTF-8 转 Unicode 后才能正常显示。

UTF-8 解决的是 UNICODE 的 效率低 的问题。

 

编程时的 ANSI 和 UNICODE:

和编码相关的概念有如下几个:宏 UNICODE , 宏 _UNICODE 单字节字符集多字节字符集标准C运行时库(crt) 操作系统封装的字符库

单字节字符集是指ANSI,windows下如果使用 char char* 这些来表示字符和字符串的话,那么使用的就是ANSI标准,因为char是8字节表示一个字符的。

多字节字符集是指Unicode,windows下如果想使用Unicode来表示字符和字符串,那么需要使用wchar_t 和 wchar_t* 。

单字节字符集的系列操作函数是 标准C运行时库中 的ANSI版本比如strcmp , strcpy ,strcat 等等,这些函数只能操作单字节字符。

多字节字符集的系列操作函数是 标准C运行时库中 的Unicode版本,比如 wcscmp ,wcscpy , wcscat 等等,这些函数只能操作多字节字符集,注意wcs系列函数中的长度相关字段的长度  是指多少个 "字符",而不是 "字节"

所以 char , char* 是不能用 wcscmp 、wcscpy 、wcscat 这些函数操作的。同理 wchar_t 和wchar_t* 也不能用 ANSI 版本的 C标准库操作。

在表示一个常量字符串时,ANSI 字符集的表示方法是  "这是一个字符串" ,而UNICODE 字符集的表示方法是 L "这是一个字符串" 。

字符串前面的 L 用来告知编译器,后面的字符串要按照UNICODE 的格式进行编译。

宏 UNICODE 是Windows专用的开关宏,宏 _UNICODE 是 标准C运行时库(crt) 专用的开关宏,一般情况下都是同时打开和关闭的。

 

如果想编写跨平台的代码,且既能支持 Unicode 又能支持 ANSI ,需要注意如下几点:

1)如果使用C运行时库,头文件为TChar.h , 使用TCHAR 代替 char 和 wchar_t,TCHAR是一个宏,当定义了_UNICODE宏 的时候,会被转换成 wchar_t,反之为char,其对应的指针类型是 TCHAR * ;

2)如果使用C运行时库,头文件为TChar.h , 使用 TCHAR* str = _TEXT("常量字符串") 代替 char* str = "常量字符串" 和 wchar_t* str = L"常量字符串" ,_TEXT是一个宏,当定义了_UNICODE宏 的时候 会被扩展成 L##宏参数(宏中的 ## 表示字符串拼接 ,否则直接返回宏参数 ;

注:使用 C 运行时库,可以实现跨平台的代码,即使用 TCAHR 和 _TEXT 编写的代码在 Windows 和 Linux 上都能正常编译运行。

3)使用 TCHAR 类型时,请使用 TChar.h 中配套的字符串处理函数;

4)当设计到字符串长度计算时,需要注意 一个 TCHAR 并不一定是一个字节,在UNICODE环境下是 2或4个字节,在ANSI环境下是 1个字节,因此使用malloc时,需要 写成  malloc(nChar*sizeof(TCAHR)) ;

 

 

如果编写只运行在Windows上的程序,且既能支持 Unicode 又能支持 ANSI ,需要注意如下几点:

1)上面使用C运行时库的方案也可用;

2)如果编写的程序只在Windows上编译运行,则可以使用 Windows 的 Unicode 库 , 使用 PTSTR 代替 char* 和 wchar_t* ,PCTSTR 代替 const char* 和 const wchar_t* ,PTSTR 和 PCTSTR 和 TCHAR 一样都是宏,具体的实现也没差别,需要注意的是,对应的开关宏不再是 _UNICODE 宏而是 UNICODE宏 ;

  _UNICODE 宏是 标准C运行时库 的开关宏,UNICODE宏是 Windows 的Unicode库的开关宏;

3)如果编写的程序只在Windows上编译运行,请使用与 PTSTR 和 PCTSTR 等等这些 Windows专用类型相对应的 字符串处理函数系统功能函数,这些函数很好辨认,一般都是提供两个版本 ,比如 CreateFileExW表示Unicode 版本, CreateFileExA 表示ANSI版本,同样地,为了实现既支持 Unicode 又支持 ANSI ,CreateFileEx 作为一个宏封装融合了 CreateFileExW 和 CreateFileExA,开关是 UNICODE 宏

字符串处理函数( 头文件为ShlWApi.h ):

对标的是 标准C运行时库 中的 TChar.h中的字符串处理函数,比如有 StrCat 与 _tcscat 对应,StrCpy 与 _tcscpy 对应。同时,StrCat也是一个宏,其根据是否定义了 UNICODE宏会被扩展成 StrCatA 和 StrCatW,其他 StrXxx函数同理。

另外,Windows还提供了 StrXxx 的别名版本,lstrcat、lstrcmp、lstrcmpi、lstrcpy 和 lstrlen,这些其实也是宏,和 对应的StrXxx一样,是对 StrXxxA 和 StrXxxW 的封装,可能是为了版本接口的兼容吧,我们在使用的使用使用 StrXxx即可。

系统功能函数:

一套用于处理入参是 ANSI 字符串,一套用于处理入参是 UNICODE 字符串。比如  CreateFileExW 和 CreateFileExA , 他们会被CreateFileEx 包裹。 

 

标准C运行时库是怎么做的呢???

标准C运行时库不会进行 A 调用 W的操作,而是各自具有自己的实现,嗯,很硬核....

4)如果确实只想在Windows下运行,请使用 Windows 的Unicode库处理 字符串,因为他们会在操作运行时就被 Exeplorer.exe 使用,且常驻RAM中,因此调用效率更高;

5)Unicode 和 ANSI 之间的转换,MultiByteToWideChar (ANSI  ===>>> UNICODE) ,WideCharToMutiByte (ANSI  <<<=== UNICODE) 。

 

Windows下A 版本和 W版本函数的底层实现:

自windows 2000开始,操作系统都是基于Unicode字符集开发的,因此如果我们传一个ANSI字符串(不适用L修饰)给系统函数,那么操作系统需要自行把ANSI字符串转换成Unicode,然后内核才能处理。

为了进行兼容,A版本的函数底层是调用W版本的函数,只不过在调用之前进行了一次把ANSI字符串转换成Unicode字符串的转化,然后在根据是否通过参数传出字符串,再讲Unicode转换成ANSI。

因此,无论是使用ANSI 字符串还是 Unicode字符串,编译和运行都是没问题的,唯一需要注意的是不要传ANSI字符串给W版本,因为W版本不会进行Unicode到ANSI的转换。如果传Unicode给A版本,A版本的转换流程将不会触发。

转换带来的是执行效率的降低和不稳定性的提高,所以尽可能使用Unicode进行编程(使用 L 或者 _TEXT 修饰常量字符串)。

如果我们编写 dll 或 COM组件,那么请务必提供两套接口,一套接收 ANSI 字符串,另一套接收 Unicode字符串,接收ANSI字符串的版本模仿A版本的实现 —— 先把ANSI转换成 Unicode然后调用 W版本。

 

推荐使用的字符串处理库:

跨平台 使用 StrSafe.h : 此库是对crt的封装,具备跨平台特性,且弥补的crt库中函数的缺陷。

Windows下 使用 ShlWApi.h  :  此库是windows特有的。

 

建议小结:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值