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(宽字节字符集);多字节字符集中定义宽字节变量的更多相关文章

  1. 关于Unicode,字符集,字符编码,每个程序员都应该知道的事

    关于Unicode,字符集,字符编码,每个程序员都应该知道的事 作者:Jack47 李笑来的文章如何判断一个人是否聪明?中提到: 必要.清晰.且准确的概念,是一切思考的基石.所谓思考,很大程度上,就是 ...

  2. 关于Unicode,字符集,字符编码

    基本概念 字符[character] 字符代表了字母表中的字符,标点符号和其他的一些符号.在计算机中,文本是由字符组成的. 字符集合[character set] 由一套用于特定用途的字符组成,例如支 ...

  3. BOM的来源是不可能出现的字符,GB2312双字节高位都是1,Unicode理论的根本缺陷导致UTF8的诞生

    Unicode字符编码规范   http://www.aoxiang.org 2006-4-2 10:48:02Unicode是一种字符编码规范 . 先从ASCII说起.ASCII是用来表示英文字符的 ...

  4. 通过编写串口助手工具学习MFC过程——(三)Unicode字符集的宽字符和多字节字符转换

    通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...

  5. 字符集研究之多字节字符集和unicode字符集

    作者:朱金灿 来源:http://blog.csdn.net/clever101 本文简介计算机中两大字符集:多字节字符集和unicode字符集的出现及关系. 首先我们须要明确的是计算机是怎样找到字符 ...

  6. VC++中多字节字符集和Unicode之间的互换

    在Visual C++.NET中,默认的字符集是Unicode,这和Windows默认的字符集是一致的,不过在老的VC6.0等工程中,默认的字符集形式是多字节字符集(MBCS:Multi-Byte C ...

  7. Unicode其实是Latin1的扩展。只有一个低字节的Uncode字符其实就是Latin1字符——附各种字符编码表及转换表

    一.概念 1,ASCII             ASCII(American Standard Code for Information Interchange),中文名称为美国信息交换标准代码.是 ...

  8. 刨根究底字符编码之十——Unicode字符集的字符编码方式CEF

    Unicode字符集的字符编码方式CEF 一.字符编码方式CEF的选择 1. 由于Unicode字符集非常大,有些字符的编号(码点值)需要两个或两个以上字节来表示,而要对这样的编号进行编码,也必须使用 ...

  9. 关于MultiByteToWideChar与WideCharToMultiByte代码测试(宽字符与多字节字符的转换)以及字符串的转换代码测试

    #pragma once #include <stdio.h> //getchar() #include <tchar.h> #include <stdlib.h> ...

随机推荐

  1. 2019 ICCV、CVPR、ICLR之视频预测读书笔记

    2019 ICCV.CVPR.ICLR之视频预测读书笔记 作者 | 文永亮 学校 | 哈尔滨工业大学(深圳) 研究方向 | 视频预测.时空序列预测 ICCV 2019 CVP github地址:htt ...

  2. python爬虫——requests库使用代理

    在看这篇文章之前,需要大家掌握的知识技能: python基础 html基础 http状态码 让我们看看这篇文章中有哪些知识点: get方法 post方法 header参数,模拟用户 data参数,提交 ...

  3. kubernetes concepts (一)

    Concepts The Concepts section helps you learn about the parts of the Kubernetes system and the abstr ...

  4. 初探ASP.NET Core 3.x (3) - Web的运作流程和ASP.NET Core的运作结构

    本文地址:https://www.cnblogs.com/oberon-zjt0806/p/12215717.html 注意:本篇大量地使用了mermaid绘制图表,加载需要较长的时间,请见谅 [TO ...

  5. requests请求

    requests:伪造浏览器请求 请求 1.get requests.get( url='', params={ 'k1': ''v1, 'k2': 'v2' } ) 即 url?k1=k2& ...

  6. StampedLock的理解和使用

    StampedLock介绍 StampedLock是为了优化可重入读写锁性能的一个锁实现工具,jdk8开始引入 相比于普通的ReentranReadWriteLock主要多了一种乐观读的功能 在API ...

  7. SpingMvc复杂参数传收总结

    上一篇文章[javaWeb传收参数方式总结]总结了简单传收参数,这一篇讲如何传收复杂参数,比如Long[] .User(bean里面包含List).User[].List.List<Map< ...

  8. java中常用的锁机制

    基础知识 基础知识之一:锁的类型 锁就那么几个,只是根据特性,分为不同的类型 锁的概念 在计算机科学中,锁(lock)或互斥(mutex)是一种同步机制,用于在有许多执行线程的环境中强制对资源的访问限 ...

  9. Python Selenium定位元素常用解决办法

       在做web应用的自动化测试时,定位元素是必不可少的,这个过程经常会碰到定位不到元素的情况(报selenium.common.exceptions.NoSuchElementException), ...

  10. leetcode 全解(python版)

    本着blabla的精神,在这里(可能会)给出leetcode的全部习题答案 --持续更新中... 1.给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整 ...