Unicode和字符串处理

23 篇文章 22 订阅
      我们熟悉的ASCII码全称是美国国家信息交换标准码,它起源于20世纪50年代末,并于1967年最终定型。ASCIIS码使用7位(bit)的宽度,有26个小写字母,26个大写字母,10个数字,32个符号,33个控制码,一个空格码,共128个代码。
      ASCII的使用相当普及,是一种非常可靠的标准。但是,ASCII是一个美国的标准,它甚至满足不了其他英语国家的需求,例如,ASCII码并没有英镑符号。
      我们知道,一些语言文字系统(例如中国的汉字)的字符集有非常多的符号,但一个字节最多只能表示256个符号,这是远远不够的。为了支持这些文字系统,双字节字符集(doube-byte character set, DBCS)应运而生。在双字节字符集中,一个字符由1个或2个字节组成。对程序员来说,和双字节字符集打交道就如同一场噩梦,因为程序员需要判断每个字节是否双字节的前导字节。
      Unicode是1988年由Apple与Xerox共同建立的一项标准。与DBCS的混乱不同,Unicode统一使用16位进行编码,即UTF-16编码。UTF的全称是Unicode Transformation Format(Unicode转换格式)。UTF-16将每个字符编码为2个字节(16位)。这样一来,程序员在处理这种格式的编码时,就显得相对简单。
      除了UTF-16编码,还有其他用于表示字符的UTF标准。
UTF-8
UTF-8将一些字符编码为1个字节,一些字符编码为2个字节,一些字符编码为3个字节,一些字符编码为4个字节。值在0x0080以下的字符压缩为1个字节,这对美国使用的字符非常适合。0x0080和0x07ff之间的字符转换为2个字节,这对欧洲和中东地区的语言非常适合。0x0800以上的字符都转换为3个字节,适合东亚地区的语言。使用代理对(surrogate pair)转换为4个字节。UTF-8是一种非常流行的编码格式。
UTF-32
UTF-32将所有的字符都编码为4个字节。从使用的角度来看,由于所有字符的字节数一定,所以处理起来较为简单。但从内存使用的角度来看,UTF-32并不是一种高效的编码方式。因此,在网络传输等场合,很少会使用UTF-32这种编码格式。UTF-32一般在应用程序内部使用。

      在谈到Unicode时,除非特别声明,我们一般是指UTF-16编码。

一、char数据类型

      我们知道,C语言用char数据类型来表示一个8位ANSI字符。当在代码中声明一个字符串时,C编译器会把字符串中的字符转换成由8位char数据类型组成的数组。例如,
char c = 'A';
char szBuffer[100] = "A String";
      可以定义一个指向字符串的指针: 
char *p;
      由于windows是一个32位的系统,指针变量p需要4个字节的存储空间。也可以定义并初始化一个指向字符串的指针:
char *p = "Hello!";
      变量p和之前一样,也只是需要4个字节的空间。字符串存储在静态内存中并使用7个字节来存储——其中6个字节存储字符串,另一个字节存储结束的'\0'。

二、wchat_t类型

      Microsoft的C/C++编译器定义了一个内建的数据类型wchat_t,表示一个16位的Unicode(UTF—16)字符。
      声明Unicode字符和字符串的方法如下:
wchar_t c = L'A';
wchar_t szBuffer[100] = L"A String";
      字符串前面的大写字母L通知编译器该字符串应当编译为一个Unicode字符串。当编译器将此字符串放入程序的数据段时,会使用UTF-16来编码每个字符。
      为了与C语言有一些区分,Windows开发团队希望定义自己的数据类型。于是,他们定义了以下数据类型:
typedef char CHAR;       // 一个8位的字符
typedef wchar_t WCHAR;   // 一个16位的字符
      除此之外,Windows还定义了一系列为我们提供方便的数据类型,可以用它们来处理字符指针和字符串指针:
// 指向8位字符(串)的指针
typedef CHAR *PCHAR;
typedef CHAR *PSTR;
typedef CONST CHAR *PCSTR;

// 指向16位的字符(串)指针
typedef WCHAR *PWCHAR;
typedef WCHAR *PWSTR;
typedef CONST WCHAR *PCWSTR;

三、维护一个源代码

      在写代码的时候,可以使用ANSI 或Unicode字符/字符串。为使其能通过编译,windows定义了以下的类型的宏:

#ifdef UNICODE

typedef WCHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST WCHAR *PCTSTR;

#define __TEXT(quote) L##quote

#else

typedef CHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST CHAR *PCTSTR;

#define __TEXT(quote) quote

#endif

#define TEXT(quote) __TEXT(quote)

      利用这些类型和宏,无论使用ANSI还是Unicode,都可以通过编译。
// 若定义了UNICODE,则作用16位的字符,否则使用8位的字符
TCHAR C = TEXT('A');

// 若定义了UNICODE,则作用16位的字符串,否则使用8位的字符串 
TCHAR szBuffer[100] = TEXT("A String");

四、Windows中的Unicode函数和ANSI函数

      如果一个Windows函数的参数列表中有字符串,则该函数通常有两个版本。例如MessageBox函数,它有两个入口点,一个名为MessageBoxA接受ANSI字符串,一个名为MessageBoxW接受Unicode字符串。
      MessageBoxA的定义如下:
int WINAPI MessageBoxA (HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
MessageBoxW的定义如下:
int WINAPI MessageBoxW (HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);
      注意第二第三个参数分别指向8位和16位的字符串。
      在编写代码时,我们只需使用MessageBox,根据是否已定义UNICODE标识符,会自动选择MessageBoxA函数还是MessageBoxW函数。
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA

五、C运行库中的Unicode函数和ANSI函数

      在C运行库中,strlen是一个返回ANSI字符串长度的函数,与之对应的是wcslen函数,它返回的是Unicode字符串的长度。
      为方便使用,定义了以下的宏:
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
      这样,只需要代码中使用_tcslen,即可以获得字符串的长度。

六、推荐的字符和字符串处理方式

1. 将文本字符串想象为字符的数组,而不是char或者字节的数组
2. 使用通用的数据类型(如TCHAR/PTSTR)来表示文本字符和字符串
3. 用明确的数据类型(如BYTE或PBYTE)表未字节,字节指针和数据缓冲区
4. 用TEXT或_T宏来表示字面常字符和字符串,但为了保持一致,请避免混用
5. UNICODE和_UNICODE符号要么同时指定,要不都不指定
6. 避免使用printf系列函数,尤其不要使用%s和%S来进行ANSI与Unicode字符串之间的转换,正确的做法是使用MultiByteToWideChar和WideCharToMultiByte函数
7. 修改有关字符串的计算。例如,函数经常希望传给它的是缓冲区大小的字符数,而不是字节数,这时应使用_countof(szBuffer),而不是sizeof(szBuffer)。如果要为一个字符串分配内存块,那么请记住内存是以字节来分配的。这意味着需使用malloc(nCharacters * sizeof(TCHAR)),而不是调用malloc(nCharacters)。
  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值