使用Unicode(宽字节字符集);多字节字符集中定义宽字节变量
2012-03-25 14:54 (分类:计算机程序)
2.2 宽字符和C
宽字符不一定是Unicode。Unicode是宽字符集的一种。然而,因为本书的焦点是Windows而不是C执行的理论,所以书中把宽字符和Unicode作为同义语。
2.2.1 char数据类型
(1)下面的语句定义并初始化了一个只包含一个字符的变量:
char c = 'A' ;
变量c需要1个字节来保存,并将用十六进位数0x41初始化,这是字母A的ASCII代码。
(2)可以这样定义一个指向字串的指针:
char * p ;
因为Windows是一个32位元作业系统,所以指针变量p需要用4个字节保存。还可初始化一个指向字串的指针:
char * p = "Hello!" ;
字串”Hello!”保存在静态记忆体中并占用7个字节,其中6个字节保存字串,另1个字节保存终止符号0。
(3)可以这样定义字符数组:
char a[10] ;
在这种情况下,编译器为该数组保留了10个字节的储存空间。运算式sizeof(a) 将返回10。
(4)如果数组是全体变量(即在所有函数外定义),您可使用像下面的语句来初始化一个字符数组:
char a[] = "Hello!" ;
(5)如果您将该数组定义为一个函数的区域变量,则必须将它定义为一个static变量,如下:
static char a[] = "Hello!" ;
无论哪种情况,字串都储存在静态程序记忆体中,并在末尾添加0,这样就需要7个字节的储存空间。
2.2.2 宽字符
(1)Unicode或者宽字符都没有改变char数据类型在C中的定义。
(2)C中的宽字符基於wchar_t数据类型,它在几个头文件包括WCHAR.H中都有定义,如下:
typedef unsigned short wchar_t ;
因此,wchar_t数据类型与无符号短整数型相同,都是16位宽。
(3)要定义包含一个宽字符的变量,可使用下面的语句:
wchar_t c = 'A' ;
变量c是一个双字节值0x0041,是Unicode表示的字母A。(然而,因为Intel微处理器从最小的字节开始储存多字节数值,该字节实际上是以0x41、0x00的顺序保存在记忆体中。如果检查Unicode文字的电脑储存应注意这一点。)
(4)可定义指向宽字串的指针:
wchar_t * p = L"Hello!" ;
注意紧接在第一个引号前面的大写字母L(代表「long」)。这将告诉编译器该字串按宽字符保存——即每个字符占用2个字节。通常,指针变量p要占用4个字节,而字串变量需要14个字节-每个字符需要2个字节,末尾的0还需要2个字节。
(5)可以用下面的语句定义宽字符数组:
static wchar_t a[] = L"Hello!" ;
该字串也需要14个字节的储存空间,sizeof (a) 将返回14。索引数组a可得到单独的字符。a[1] 的值是宽字符「e」,或者0x0065。第一个引号和L两个符号之间必须没有空格。L使编译器将字串存为每个字符2字节。
(6)可在单个字符文字前面使用L字首,来表示它们应解释为宽字符。如下所示:
wchar_t c = L'A' ;
但通常这是不必要的,C编译器会对该字符进行扩充,使它成为宽字符。
2.2.3 宽字符程序库函数
strlen函数的宽字符版是wcslen(wide-character string length:宽字串长度),并且在STRING.H(其中也说明了strlen)和WCHAR.H中均有说明。strlen函数说明如下:
size_t __cdecl strlen (const char *) ;
而wcslen函数则说明如下:
size_t __cdecl wcslen (const wchar_t *) ;
要得到宽字串的长度可以调用
wchar_t * pw = L"Hello!" ;
iLength = wcslen (pw) ;
函数将返回字串中的字符数6。改成宽字节後,字串的字符长度不改变,只是字节长度改变了。
所有带有字串参数的C执行时期程序库函数都有宽字符版。例如,wprintf是printf的宽字符版。这些函数在WCHAR.H和含有标准函数说明的头文件中说明。
2.2.4 维护单一原始码
使用Unicode最主要缺点是,程序中的每个字串都将占用两倍的储存空间。此外,宽字符执行时期程序库中的函数比常规的函数大。
因此,就有必要建立两个版本的程序——一个处理ASCII字串,另一个处理Unicode字串。最好的解决办法是维护既能按ASCII编译又能按Unicode编译的单一原始码档案。
一个办法是使用Microsoft Visual C++包含的TCHAR.H头文件。(该头文件不是ANSI C标准的一部分,因此其中定义的每个函数和巨集定义的前面都有一条下横线。)
TCHAR.H为需要字串参数的标准执行时期程序库函数提供了一系列的替代名称(例如,_tprintf和_tcslen)。有时这些名称也称为「通用」函数名称,因为它们既可以指向函数的Unicode版也可以指向非Unicode版。
如果定义了名为_UNICODE的识别字,并且程序中包含了TCHAR.H头文件,那么_tcslen就定义为wcslen:
#define _tcslen wcslen
如果没有定义UNICODE,则_tcslen定义为strlen:
#define _tcslen strlen
等等。
TCHAR.H还用一个新的数据类型TCHAR来解决两种字符数据类型的问题。如果定义了 _UNICODE识别字,那么TCHAR就是wchar_t:
typedef wchar_t TCHAR ;
否则,TCHAR就是char:
typedef char TCHAR ;
现在开始讨论字串文字中的L问题。如果定义了_UNICODE识别字,那么一个称作__T的巨集就定义如下:
#define __T(x) L##x
这是相当晦涩的语法,但合乎ANSI C标准的前置处理器规范。那一对井字号称为「粘贴符号(token paste)」,它将字母L添加到巨集引数上。因此,如果巨集引数是"Hello!",则L##x就是L"Hello!"。
如果没有定义_UNICODE识别字,则__T巨集只简单地定义如下:
#define __T(x) x
此外,还有两个巨集与__T定义相同:
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
在Win32 console程序中使用哪个巨集,取决於您喜欢简洁还是详细。基本地,必须按下述方法在_T或_TEXT巨集内定义字串文字:
_TEXT ("Hello!")
这样做的话,如果定义了_UNICODE,那么该串将解释为宽字符的组合,否则解释为8位元的字符字串
_T会根据你工程的设置自动转换UNICODE和非UNICODE.
L就是转为UNICODE
Visual C++里边定义字符串的时候,用_T来保证兼容性,是一种数据类型,但是它不会产生结果,被编译系统的预处理系统来解释,VC支持ascii和unicode两种字符类型,用_T可以保证从ascii编码类型转换到unicode编码类型的时候,程序不需要修改。
如果将来你不打算升级到unicode,那么也不需要_T!
_T是将字符串转换为TCHAR,TCHAR是一个宏定义,当定义了UNICODE时TCHAR等同于WCHAR,否则等同于CHAR。为了和以后的平台兼容,建议使用TCHAR,而不要使用普通的CHAR。例子:TCHAR *s = _T("FSDF")
L将字符串转换为WCHAR,用于需要UNICODE的环境。例子:WCHAR *s = L"FSDF"
此外,_TEXT 和_T 一样的
Example:
SetWindowText(_T("我很好"));
在中文Win2000上正常,在英文Win2000下就是乱码!
// _T()自动将()内字符串转成unicode or Multibyte-character or SBCS (ASCII) 根据系统宏定义,
// 为了将程序与vb等unicode 编码的程序交互,为了程序的国际化,为了...
// 看msdn! 如果定义了 UNICODE 就变成 L把字符串转换成宽字符,否则没用。
// 统一的字符编码标准, 采用双字节对字符进行编码
// _T把参数转换成当前系统支持的字符,例如支持UNICODE就转换成宽字符,否则就是单字符
#ifdef UNICODE
#define _T(x) L##x
#else
#define _T(x) x
#endif
_T/_TEXT是在TCHAR.H头文件中定义的宏。
在_UNICODE和_MBCS都没有定义时,对其后的字符串无影响
在_MBCS定义时,对其后的字符串无影响
在_UNICODE定义时,其后的字符串被定义为 L(即转换为Unicode字符)
本质上是为了生成Unicode和非Unicode通用的程序而定义的宏。
Unicode:宽字节字符集
1. 如何取得一个既包含单字节字符又包含双字节字符的字符串的字符个数?
可以调用Microsoft Visual C++的运行期库包含函数_mbslen来操作多字节(既包括单字节也包括双字节)字符串。
调用strlen函数,无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。
2. 如何对DBCS(双字节字符集)字符串进行操作?
函数 描述
PTSTR CharNext ( LPCTSTR ); 返回字符串中下一个字符的地址
PTSTR CharPrev ( LPCTSTR, LPCTSTR ); 返回字符串中上一个字符的地址
BOOL IsDBCSLeadByte( BYTE ); 如果该字节是DBCS字符的第一个字节,则返回非0值
3. 为什么要使用Unicode?
(1) 可以很容易地在不同语言之间进行数据交换。
(2) 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件。
(3) 提高应用程序的运行效率。
Windows 2000是使用Unicode从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode来开发应用程序,就能够使你的应用程序更加有效地运行。
Windows CE 本身就是使用Unicode的一种操作系统,完全不支持ANSI Windows函数
Windows 98 只支持ANSI,只能为ANSI开发应用程序。
Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。
4. 如何编写Unicode源代码?
Microsoft公司为Unicode设计了WindowsAPI,这样,可以尽量减少代码的影响。实际上,可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。
_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件。当编译源代码模块时,通常必须同时定义这两个宏。
5. Windows定义的Unicode数据类型有哪些?
数据类型 说明
WCHAR Unicode字符
PWSTR 指向Unicode字符串的指针
PCWSTR 指向一个恒定的Unicode字符串的指针
对应的ANSI数据类型为CHAR,LPSTR和LPCSTR。
ANSIUnicode通用数据类型为TCHAR,PTSTR,LPCTSTR。
6. 如何对Unicode进行操作?
字符集 特性 实例
ANSI 操作函数以str开头 strcpy
Unicode 操作函数以wcs开头 wcscpy
MBCS 操作函数以_mbs开头 _mbscpy
ANSIUnicode 操作函数以_tcs开头 _tcscpy(C运行期库)
ANSIUnicode 操作函数以lstr开头 lstrcpy(Windows函数)
所有新的和未过时的函数在Windows2000中都同时拥有ANSI和Unicode两个版本。ANSI版本函数结尾以A表示;Unicode版本函数结尾以W表示。Windows会如下定义:
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif !UNICODE
7. 如何表示Unicode字符串常量?
字符集 实例
ANSI “string”
Unicode L“string”
ANSIUnicode T(“string”)或_TEXT(“string”)if( szError[0] == _TEXT(‘J’) ){ }
8. 为什么应当尽量使用操作系统函数?
这将有助于稍稍提高应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM。
如:StrCat,StrChr,StrCmp和StrCpy等。
9. 如何编写符合ANSI和Unicode的应用程序?
(1) 将文本串视为字符数组,而不是chars数组或字节数组。
(2) 将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。
(3) 将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。
(4) 将TEXT宏用于原义字符和字符串。
(5) 执行全局性替换(例如用PTSTR替换PSTR)。
(6) 修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意味着不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)sizeof(TCHAR)。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。这就是说,应该调用malloc(nCharacters sizeof(TCHAR)),而不是调用malloc(nCharacters)。
10. 如何对字符串进行有选择的比较?
通过调用CompareString来实现。
标志 含义
NORM_IGNORECASE 忽略字母的大小写
NORM_IGNOREKANATYPE 不区分平假名与片假名字符
NORM_IGNORENONSPACE 忽略无间隔字符
NORM_IGNORESYMBOLS 忽略符号
NORM_IGNOREWIDTH 不区分单字节字符与作为双字节字符的同一个字符
SORT_STRINGSORT 将标点符号作为普通符号来处理
11. 如何判断一个文本文件是ANSI还是Unicode?
判断如果文本文件的开头两个字节是0xFF和0xFE,那么就是Unicode,否则是ANSI。
12. 如何判断一段字符串是ANSI还是Unicode?
用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode有可能返回不正确的结果。
13. 如何在Unicode与ANSI之间转换字符串?
Windows函数MultiByteToWideChar用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte将宽字符串转换成等价的多字节字符串。
使用Unicode(宽字节字符集);多字节字符集中定义宽字节变量的更多相关文章
- 关于Unicode,字符集,字符编码,每个程序员都应该知道的事
关于Unicode,字符集,字符编码,每个程序员都应该知道的事 作者:Jack47 李笑来的文章如何判断一个人是否聪明?中提到: 必要.清晰.且准确的概念,是一切思考的基石.所谓思考,很大程度上,就是 ...
- 关于Unicode,字符集,字符编码
基本概念 字符[character] 字符代表了字母表中的字符,标点符号和其他的一些符号.在计算机中,文本是由字符组成的. 字符集合[character set] 由一套用于特定用途的字符组成,例如支 ...
- BOM的来源是不可能出现的字符,GB2312双字节高位都是1,Unicode理论的根本缺陷导致UTF8的诞生
Unicode字符编码规范 http://www.aoxiang.org 2006-4-2 10:48:02Unicode是一种字符编码规范 . 先从ASCII说起.ASCII是用来表示英文字符的 ...
- 通过编写串口助手工具学习MFC过程——(三)Unicode字符集的宽字符和多字节字符转换
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 字符集研究之多字节字符集和unicode字符集
作者:朱金灿 来源:http://blog.csdn.net/clever101 本文简介计算机中两大字符集:多字节字符集和unicode字符集的出现及关系. 首先我们须要明确的是计算机是怎样找到字符 ...
- VC++中多字节字符集和Unicode之间的互换
在Visual C++.NET中,默认的字符集是Unicode,这和Windows默认的字符集是一致的,不过在老的VC6.0等工程中,默认的字符集形式是多字节字符集(MBCS:Multi-Byte C ...
- Unicode其实是Latin1的扩展。只有一个低字节的Uncode字符其实就是Latin1字符——附各种字符编码表及转换表
一.概念 1,ASCII ASCII(American Standard Code for Information Interchange),中文名称为美国信息交换标准代码.是 ...
- 刨根究底字符编码之十——Unicode字符集的字符编码方式CEF
Unicode字符集的字符编码方式CEF 一.字符编码方式CEF的选择 1. 由于Unicode字符集非常大,有些字符的编号(码点值)需要两个或两个以上字节来表示,而要对这样的编号进行编码,也必须使用 ...
- 关于MultiByteToWideChar与WideCharToMultiByte代码测试(宽字符与多字节字符的转换)以及字符串的转换代码测试
#pragma once #include <stdio.h> //getchar() #include <tchar.h> #include <stdlib.h> ...
随机推荐
- STM32F429的特点
STM32F429 内核 Crotex M4 最高主频 180MHZ FPU 有 DSP指令集 有 最大SRAM 256K 备份域SRAM 有 最大FLASH 2MB GPIO最高翻转速度 90MHZ ...
- cogs 173. 词链 字典树模板
173. 词链 ★★☆ 输入文件:link.in 输出文件:link.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述]给定一个仅包含小写字母的英文单词表,其中每个 ...
- Java架构师中的内存溢出和内存泄露是什么?实际操作案例!
JAVA中的内存溢出和内存泄露分别是什么,有什么联系和区别,让我们来看一看. 01 内存泄漏 & 内存溢出 1.内存泄漏(memory leak ) 申请了内存用完了不释放,比如一共有 102 ...
- 关于pycharm中输出的内容不全的解决办法
很多时候我们会发现有的时候输出的结果特别多的时候,会在最后输出时用...代替,最后输出一个总长度,那要咋么弄咧? import pandas as pd # 设置显示的最大列.宽等参数,消掉打印不完全 ...
- ThinkPad全家族机型对比
如图所示
- java Random类(API)
一.过程 1.导包 2.实例化 3.使用(类的成员方法) 二.作用 生成随机数,与python中random 相似 三.常用方法 1.nextInt(),随机生成int数据类型范围的数 2.nextI ...
- [hdu2255] 奔小康赚大钱
Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有 \(n\) 间房间,刚好有 \(n\) 家 ...
- 嗯 想写个demo 苦于没数据
step 1: 来点数据: 各种数据 随你便了. step 2: 来个 服务端 step 3 : 客户端 调用
- 关于MySQL5.6配置文件my-default.ini不生效问题
一.问题描述 首先,由于工作要求,需使用MySQL5.6版本(绿色版),从解压到修改root密码,一切都很顺利,但是在我要修改mysql的最大连接数的时候,出现问题了,配置不生效.完蛋.还好有万能的百 ...
- ValueError: The field admin.LogEntry.user was declared with a lazy reference to 'system.sysuser', bu
问题:已经在settings.py文件中注册过app仍旧提示没有安装,并且使用makegirations命令时会抛出如下异常. ValueError: The field admin.LogEntry ...