字符、字符串简介

来源:互联网 发布:开淘宝店电脑配置要求 编辑:程序博客网 时间:2024/05/16 08:32

1          简介

开发过程中,我常常会遇到ASCIIUnicode以及MBCS不同型的字符串,而且还需要经常进行转换操作。本文先介绍字符的编码方式、各种基本字符串型,然后明相的帮助,如CComBSTR、_bstr_t、CString和basic_string等,最后讨论们之间以及和其他的数据类型转换方法

 

2          ANSI、MBCS与UNICODE

ASCII码(American Standard Code for Information Interchange)由一个字节中的7位(bit)表示8位没有被使用,范围是0x00 - 0x7F 共128个字符。其中32--127表示字符32 是空格,32 以下是控制字符(不可见)。

后来扩展了ASCII的定义,ANSI(American National Standards Institute)使用的是一个字节的全部8位(bit)来表示字符,也就具有了 256个字符元;新扩展出来的字符主要是控制字符ANSI字符集最大一个特点是向下兼容ASCII并且留有空余位置处理一些特殊字符。

后来,很多语系(尤其是非拉丁语,比如日文、韩文、阿拉伯文、台湾繁体都使用类似的方法扩展了本地字符集的定义,现在统一称为 MBCS 字符集(多字节字符集)。这种系统中,有些字符占用 1 字节,有些 2 字节。这个方法是有缺陷的,因为各个国家地区定义的字符集有交集,因此如果使用简体中文GB-2312的软件,就不能在台湾繁体BIG-5的环境下运行(显示乱码),反之亦然。

当然,提到MBCS,就不得不提一下Code Page,即代码页。字符必须编码后才能被计算机处理,那么计算机使用的编码方式就叫做Code Page。比如适用于简体中文的GB2312和用于繁体中文的big5。

为了把全世界人民所有的文字符号都统一进行编码,于是制定了UNICODE标准字符集(宽字符集)。UNICODE 使用2个字节表示一个字符(unsigned short int、WCHAR、_wchar_t、OLECHAR),它的范围是0x0000 - 0xFFFF 共6万多个字符。这样,它把在这个星球上的每一个合理的文字系统整合成了一个单一的字符集,全世界任何一个地区的软件,可以不用修改地就能在另一个地区运行了。

 

在标准C++中,可以这样定义一个MBCS或者ANSI的字符串,即:

char* pc = "zhs";

char c = 'z';

 

定义一个UNICODE的字符串,可以用L前缀,即:

wchar_t* pww = L"zhs";

wchar_t ww = L'z';

 

总结一下:

ASCII长度为7位,共有128个字符,我们常用的英文字母和符号都包含在这里边。

ANSI长度为8位,共有256个字符。前128个字符也为ASCIIASCII和ANSI只是在一些控制符号上有区别。

MBCS长度为1或2个字节,不确定。

UNICODE16位,即2个字节,被C/C++定义成wchar_t。

 

3          UNICODE的编码方式

很多人还存在这样的误解: Unicode 仅仅是每个字符占 2个字节,所以一共有 65536 个可能的字符。然而,这是错误的。

 

最初的Unicode编码, 使用两个字节表示一个字符,包括表示字符串结束符的‘0’也是两个字节,这种编码方式叫做UTF-16。那么 "Hello" 表示为:

  00 48 00 65 00 6C 00 6C 00 6F 00 00

实际上,还有一种表示方式:

  48 00 65 00 6C 00 6C 00 6F 00 00 00

到底高位字节在前还是低位字节在前面,是两种不同的模式。这要看特定的 CPU 在何种模式下工作的更快。 所以这两种都有。详细请参考:以下是Big-Endian 和 Little-Endian 两者概念的区别

 

理论上种解决方案很不错。但是对于英语用户来说,他们很少使用 00FF 以上的字符, 有些人无法忍受采用双倍的存储空间来存储每个字符而造成的浪费。基于这些原因,很多人决定忽视 Unicode;而另外的人想出了别的方案

然后人们制定了 UTF-8 UTF-8 是用于保存 Unicode的另一套编码方案。在 UTF-8 中,任何一个 0--127 的字符占用一个字节。只有 128 以及更大的才占用 2, 3, 直到 6 个字节。

UTF-8编码的字符串,其结束符是一个字节的0;而不是UTF-16的两个字节的0。换句话说,UTF-8编码的字符串,会向下兼容标准C的函数。

 

实际上,还有一种Unicode编码形式, 使用个字节表示字符串中的任意一个字符这种编码方式叫做UTF-32,或者UCS-4

 

默认情况Windows NT操作系统的开发(尤其VC开发),如果选择的是UNICODE,指的就是UTF-16;如果选择的是MBCS,则表示字符串中既有单字节字符也有双字节字符。Intelx86机器上都是little-endian而不是big-endian方式

 

那么在实际开发中,我们为什么要使用UNICODE哪?下列一些情况下使用Unicode将会使你受益

1你的程序只运行在Windows NT系统中。Windows 9x 中大多数的 API 没有实现 Unicode 版本。所以如果你的程序要在Windows 9x中运行你必使用MBCS APIs。然而由于NT内部都使用Unicode所以使用Unicode APIs将会加快你的程序的运行速度。次,你传递一个字符串MBCS API,操作系会把个字符串转换Unicode字符串,然后对应Unicode API。如果一个字符串被返回,操作系统还要把它转变回去。尽管转换过程被高度化了,但它速度造成的失是无法避免的。

2你的程序需要处理超过MAX_PATH个字符长的文件名。只要你使用Unicode API,NT系使用非常的文件名(突破了MAX_PATH的限制,MAX_PATH=260)。使用Unicode API的另一个点是你的程序会自动处理用户输入的各种语言。所以一个用可以入英文,中文或者日文,而你不需要写代理它

3你的程序需要使用XP中引入的只有Unicode版本的API 最后,随着windows 9x品的淡出,微似乎正在抛弃MBCS APIs。例如,包含两个字符串参数的SetWindowTheme() API只有Unicode版本的。使用Unicode来build你的程序将会化字符串的理,你不必在MBCS和Unicode之相互转换

 

总结一下几种 Unicode 的表示方法

传统的双字节表示方法, 称为 UCS-2(因为有 2 个字节) 或者 UTF-16(因为有 16 个位),但是你要搞清楚是高位在前的,还是高位在后的 UCS-2。

还有一种就是 UTF-8。 如果你的程序只使用英文的话,它仍然会工作正常。

另外还有 UCS-4, 储存每一个字符为 4 个字节。它的优点是每一个字符都保存为同样长的。但很明显,缺点是浪费太多存储空间了。

 

以下是Big-Endian 和 Little-Endian 两者概念的区别,仅供参考。

Big-Endian 和 Little-Endian 字节排序

字节排序

含义

Big-Endian

一个Word中的高位的Byte放在内存中这个Word区域的低地址处。

Little-Endian

一个Word中的低位的Byte放在内存中这个Word区域的低地址处。

必须注意的是:表中一个Word的长度是16位,一个Byte的长度是8位。如果一个数超过一个Word的长度,必须先按Word分成若干部分,然后每一部分(即每个Word内部)按Big-Endian或者Little-Endian的不同操作来处理字节。

一个例子:

如果我们将0x1234abcd(这是一个INT32类型的数据)写入到以0x0000开始的内存中,则结果为:

           big-endian     little-endian
0x0000     0x12              0xcd
0x0001     0x34              0xab
0x0002     0xab              0x34
0x0003     0xcd              0x12
(注意:0xab换算成2进制是10101011,是个8位的数。)

 

4          T和TCHAR

因为我们开发面向的是具有多字节字符的中文或者日文,那么我们可能会有MBCS和UNICODE这两种选择。实际开发中,我们希望把MBCS和Unicode的区别透明化,也就是说不应该到处都有这样的代码:

#ifdef _UNICODE

 wchar_t ch;

#else

 char ch;

#endif

 

为了简化开发,微软运行时库提供了一系列的映射,把很多字符串相关的数据类型、函数和对象给映射成了T类型,这些映射的宏都在TCHAR.H中。要决定程序中应该使用的编码方式,可以通过VC环境中设定预编译的宏:

Project->Settings...->C/C++ tab页->Preprocessor definitions

对应于MBCS, Unicode,和ASCII (SBCS),分别设置为:MBCS、UNICODE,_UNICODE 和空(什么也不设)。

#define

Compiled version

Example

_UNICODE

Unicode (wide-character)

_tcsrev maps to _wcsrev

_MBCS

Multibyte-character

_tcsrev maps to _mbsrev

None ( neither _UNICODE nor _MBCS defined)

SBCS (ASCII)

_tcsrev maps to strrev

 

以下是一些T类型的定义在实际系统中的对应:

Generic-text
data type name

_MBCS
defined

_UNICODE
defined

_TCHAR

char

wchar_t

_tfinddata_t

_finddata_t

_wfinddata_t

_tfinddata64_t

__finddata64_t

__wfinddata64_t

_tfinddatai64_t

_finddatai64_t

_wfinddatai64_t

_TSCHAR

signed char

wchar_t

_TUCHAR

unsigned char

wchar_t

_TXCHAR

unsigned char

wchar_t

_T or _TEXT

No effect (removed by preprocessor)

L(converts following character or string to its Unicode counterpart)

 

对于字符串的数据类型,我们可以使用TCHAR

#ifdef _UNICODE

 typedef wchar_t TCHAR;

#else

 typedef char TCHAR;

#endif

所以,TCHAR中在MBCS程序中是char类型,在Unicode中是 wchar_t 类型。

 

对于字符串常量,有个 _T() 宏,用于解决 L 前缀:

#ifdef _UNICODE

 #define _T(x) L##x

#else

 #define _T(x) x

#endif

## 是预处理算子,将二个变量粘贴在一起。宏_T有几种形式,功能都相同。如: -- TEXT, _TEXT, __TEXT, 和 __T这四种宏的功能相同。

不管什么时候都应该对字符串用 _T 宏处理,这样就可以在Unicode编码中给字符串加上L前缀,而对ANSI则没有任何影响

TCHAR szNewText[] = _T("we love Bob!");

 

另外,Microsoft还扩展了一些常用的字符和字符串类型,其中,P表示pointC表示constT表示TCHARW表示wide。如下表所示:

type

Meaning in MBCS builds

Meaning in Unicode builds

WCHAR

wchar_t

wchar_t

LPSTR

zero-terminated string of char (char*)

zero-terminated string of char (char*)

LPCSTR

constant zero-terminated string of char (const char*)

constant zero-terminated string of char (const char*)

LPWSTR

zero-terminated Unicode string (wchar_t*)

zero-terminated Unicode string (wchar_t*)

LPCWSTR

constant zero-terminated Unicode string (const wchar_t*)

constant zero-terminated Unicode string (const wchar_t*)

TCHAR

char

wchar_t

LPTSTR

zero-terminated string of TCHAR (TCHAR*)

zero-terminated string of TCHAR (TCHAR*)

LPCTSTR

constant zero-terminated string of TCHAR (const TCHAR*)

constant zero-terminated string of TCHAR (const TCHAR*)

 

注意,这些映射的定义是微软扩展的,并非ANSI标准,所以它是不可移植的。

 

5          C风格的字符串,即:字符数组

所有的字符串类都起源于C风格的字符串

C风格字符串是字符的数组,数组中每一个元素保存了一个字符,可以是单字节或者多字节,最后以零字节表示字符串结尾;对于单字节或者MBCS字符串,一个字节0x00结束字符串,对于Unicode,则两个字节0x0000结束字符串。

C语言字符串处理函数,如strcpy(), sprintf(), atol()等只能用于单字节字符串。在标准库中有只用于Unicode字符串的函数,如wcscpy(), swprintf(), _wtol()。微软在C运行库(CRT)中加入了对MBCS字符串的支持使用_mbsxxx()函数,所以在处理DBCS字符串(如日语,中文,或其它MBCS)时,就要用_mbsxxx()函数。

 

现在用一个示例来说明字符串处理函数的不同。如有Unicode字符串L"Bob":

对于strxxx() 和 _mbsxxx() 函数族中的字符串长度测量函数,它们都返回字符串的字符数。如果字符串含有3个双字节字符,_mbslen()将返回3Unicode的函数返回的是wchar_t的数量,如wcslen(L"Bob") 返回3。

这时如用strlen()函数求字符串的长度就发生问题。函数找到第一个字节42,然后是00,意味着字符串结尾,于是返回1。而如果用Unicode的函数,wcslen(L"Bob"),则返回3,因为他判断的是双字节的00。

 

我们中的大多数人都是从单字节字符集成长过来的,都习惯于用指针的 ++ 和 -- 操作符来遍历字符串,有时也使用数组来处理字符串中的字符。这二种方法对于纯粹的单字节字符串 Unicode 字符串的操作都是正确无误的,因为二者的字符都是等长的,编译器能够的正确返回我们寻求的字符位置。

但对于MBCS字符串就不能这样了,因为每一个字符的长度不确定所以,用指针访问MBCS字符串不可使用 ++ 算子,除非每次都检查是否为前导字节绝不可使用 -- 算子来向后遍历。正确的方法是用MBCS函数将指针指向恰当的字符位置,比如CharPrev()CharNext()函数,它会根据情况而决定移动一个或者两个字节

 

6          STL的string、wstring和其他

STL只有一个字符串类,即basic_string模板类,basic_string管理一个零结尾的字符数组,字符类型char或者wchar_t由模板参数决定。

basic_string预定义了二个特例:string,含有char类型字符;wstring,含有wchar_t类型字符。这两个类型是如下定义的:

   typedef basic_string<wchar_t> wstring;

   typedef basic_string<char> string;

没有内建的TCHAR特例,可用下面的代码自己实现:

   typedef basic_string<TCHAR> tstring;

 

basic_string模板类提供了和CString一样的功能,而且还有其他的优势:CString(是MFC的)的效率高;它是标准C++库的内容,易于移植

 

如何使用string和wstring。

首先需要包含头文件<string>,然后引用名字空间using namespace std;之后就像普通的类一样使用。比如,要使用replace功能,可以如下使用:

#include <string>

 

{

   using namespace std;

 

   string result1a, result1b;

   string s1o ( "AAAAAAAA" );

   string s1p ( "BBB" );

   result1a = s1o.replace ( 1 , 3 , s1p );

}

关于详细介绍,请参看另外的一篇文档< STLString.doc >

关于详细介绍,还可以参看另外的一篇文档< The C++ Programming Language, by Bjarne Stroustrup >,第20”Stgrings”

 

7          MFC中的CString及其他

CStringMFC库提供的类,它保存并管理一个TCHAR数组,它的实际字符类型取决于预处理标记的设置。

CString比STL字符串优越的是它的构造函数接受MBCS和Unicode字符串,并且可以转换为LPCTSTR,因此可以向接受LPCTSTR的函数直接传递CString对象,不必std::string那样调用c_str()方法;另外,它还可以从字符资源表中加在字符串来进行构造。

 

CString的使用,要比string简单,因为如果我们创建了一个VC工程,默认的就已经包含了CString的类,而且也不需要引用名字空间。

 

// 构造

CString s1 = "char string"; // 从LPCSTR构造

CString s2 = L"wide char string"; // 从LPCWSTR构造

CString s3 ( ' ', 100 ); // 预分配100字节,填充空格

CString s4 = "New window text";

CString s6, s7;

s6.LoadString ( IDS_SOME_STR ); // 从字符串表加载

s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... ); // 从字符串表加载打印格式的字符串

 

//显示或者隐式的类型转化

SetWindowText ( hwndSomeWindow, s4 );   // 隐式转化成LPCTSTR

SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 ); // 或者,显式地做强制类型转换

 

注意,CString只允许一种强制类型转换,即强制转换为LPCTSTR。强制转换为LPTSTR (非常量指针)是错误的。按照老习惯,将CString强制转换为LPTSTR只能伤害自己。有时在程序中没有发现出错,那只是碰巧。转换到非常量指针的正确方法是调用GetBuffer()方法,这种方法是把CString中的字符串指针拿出来,如果用户修改这段内容,CString中的内容也相应地被修改了,这种方法在没有强烈需求的情况下也尽量不使用。进行非常量的强制类型转换,打破了面向对象的封装原则,并逾越了CString的内部操作,容易出Bug

 

关于详细介绍,请参看另外的一篇文档< CStringClass.doc >

 

8          COM中的BSTR、CComBSTR、_bstr_t和其他

COM中对字符串有特殊的要求,即:宽字符,告知对方字符串的长度。
 
OLECHARwin32中为16位,即2个字节,被C/C++定义成wchar_t
OLECHARwin16中为8位,即1个字节,被C/C++定义成char
当然,我们的开发都是基于Win32的,所以我们在开发过程中会认为OLECHAR就是被定义成了wchar_t
 

8.1BSTR

BSTR是一个指向UNICODE 字符串的指针,这个字符串可以没有结束符,在BSTR向前的4个字中,使用DWORD保存着个字符串的字节长度。也就是说,它的长度不是由NULL结尾字符决定,而是由长度前缀决定,虽然BSTR也可能有NULL字符在内部或者结尾处。它主要用在COM以及Automation方面。
看看定义就知道了:
typedef wchar_t WCHAR;
typedef WCHAR OLECHAR;
typedef OLECHAR* BSTR;
 
注意:BSTR不等于OLECHAR*,最大的区别在于它有一个长度前缀。
 
通俗地说,你不能直接把一个内存指针直接作为参数传递给COM函数。系统需要把这块内存的内容传递到地球另一边的计算机上,因此,系统至少需要知道你这块内存的尺寸,也就是说,需要传递多少字节给用户。而字符串又是非常常用的一种类型,因此 COM 设计者引入了BSTR。因此系统就能够正确处理并传送这个字符串到地球另一 边了。特别需要注意的是,由于BSTR的指针就是指向 UNICODE 串,因此 BSTR  LPOLESTR 可以在一定程度上混用,但是不建议这样做。

 

关于BSTR的详细介绍,请参看另外的一篇文档< BSTR.doc >

8.2_bstr_t

_bstr_t C++运行时库提供的对BSTR的完全包装类。实际上,它隐含了BSTR的大多数细节,它提供多种构造函数,能够处理隐含的C类型字符串以及BSTR字符串。它虽然提供了BSTR的处理机制,但是不能作为COM方法的输出参数[out],因为他的操作符重载只有wchar_t而没有BSTR;他可以作为输入传递给需要BSTR数据的函数,但是,不建议这样做。如果要用到BSTR* 类型数据,用ATLCComBSTR类更为方便
 
_bstr_t使用示例如下:
// 构造
_bstr_t bs1 = _T("wide char string"); // LPCWSTR构造
_bstr_t bs3 = bs1;              // 拷贝另一个 _bstr_t
 
// 数据萃取
LPCTSTR pwsz1 = bs1;            // 返回内部的Unicode字符串
BSTR    bstr = bs1.copy();      // 拷贝bs1, 返回BSTR
SysFreeString ( bstr );
 

8.3CComBSTR

CComBSTR ATLBSTR包装类,某些情况下比_bstr_t 更有用。最主要的是,CComBSTR允许操作隐含BSTR,就是说,传递一个CComBSTR对象给COM方法时,CComBSTR对象会自动管理BSTR内存。例如,要调用下面的接口函数:
// 简单接口
struct IStuff : public IUnknown
{
  STDMETHOD(SetText)(BSTR bsText);
  STDMETHOD(GetText)(BSTR* pbsText);
};
CComBSTR 有一个BSTR操作方法,能将BSTR直接传递给SetText()。还有一个引用操作(operator &)方法,返回BSTR*,将BSTR*传递给需要它的有关函数。
CComBSTR bs1;
CComBSTR bs2 = _T("new text");
pStuff->GetText ( &bs1 );       // ok, 取得内部BSTR地址
pStuff->SetText ( bs2 );        // ok, 调用BSTR转换
pStuff->SetText ( (BSTR) bs2 ); // cast ok, 同上
 
CComBSTR有类似于 _bstr_t 的构造函数。但没有内建MBCS字符串的转换函数。可以调用ATL宏进行转换。
// 构造
CComBSTR bs3 = bs1; // 拷贝CComBSTR
CComBSTR bs4;
bs4.LoadString ( IDS_SOME_STR ); // 从字符串表加载
// 数据萃取
BSTR bstr1 = bs1; // 返回内部BSTR,但不可修改!
BSTR bstr2 = (BSTR) bs1; // cast ok, 同上
BSTR bstr3 = bs1.Copy(); // 拷贝bs1, 返回BSTR
BSTR bstr4;
bstr4 = bs1.Detach(); // bs1不再管理它的BSTR
// ...
SysFreeString ( bstr3 );
SysFreeString ( bstr4 );
 
上面的最后一个示例用到了Detach()方法。该方法调用后,CComBSTR对象就不再管理它的BSTR或其相应内存。所以bstr4就必须调用SysFreeString()
最后讨论一下引用操作符(operator &)。它的超越使得有些STL集合(list)不能直接使用CComBSTR。在集合上使用引用操作返回指向包容类的指针。但是在CComBSTR上使用引用操作,返回的是BSTR*,不是CComBSTR*。不过可以用ATLCAdapt类来解决这个问题。例如,要建立一个CComBSTR的队列,可以声明为:
  std::list< CAdapt<CComBSTR>> bstr_list;
CAdapt 提供集合所需的操作,是隐含于代码的。这时使用bstr_list 就象在操作一个CComBSTR队列。

 

关于详细介绍,请参看另外的一篇文档< CCOMBSTR.doc >,目前还没有写完。

 

9          字符串的处理函数以及常用算法

首先介绍一些通用函数命名规则:

字符集

特性

实例

ANSI

操作函数以str开头

strcpy

Unicode

操作函数以wcs开头

wcscpy

MBCS

操作函数以_mbs开头

_mbscpy

ANSI/Unicode

操作函数以_tcs开头(C运行期库)

_tcscpy

ANSI/Unicode

操作函数以lstr开头(Windows函数)

lstrcpy

 

下表列出了一些运行时库的字符串处理API(比较常用的用浅蓝色标出)。这些API是针对C风格的字符串的处理函数,如果你用到了CComBSTR_bstr_tCString等,就使用这些类提供的成员函数就好了,各组函数之间在功能上基本上没有大的差别,只是实现方式的一些差异。

Routine

Use

_mbsdec, _strdec, _wcsdec

Move string pointer back one character

_mbsinc, _strinc, _wcsinc

Advance string pointer by one character

_mbsnextc, _strnextc, _wcsnextc

Find next character in string

_mbsninc. _strninc, _wcsninc

Advance string pointer by n characters

_mbsspnp, _strspnp, _wcsspnp

Return pointer to first character in given string that is not in another given string

_scprintf, _scwprintf

Return the number of characters in a formatted string

_snscanf, _snwscanf

Read formatted data of a specified length from the standard input stream.

sprintf, _stprintf

Write formatted data to a string

strcat, wcscat, _mbscat

Append one string to another

strchr, wcschr, _mbschr

Find first occurrence of specified character in string

strcmp, wcscmp, _mbscmp

Compare two strings

strcoll, wcscoll, _stricoll, _wcsicoll, _strncoll, _wcsncoll, _strnicoll, _wcsnicoll

Compare two strings using current locale code page information (_stricoll, _wcsicoll, _strnicoll, and _wcsnicoll are case-insensitive)

strcpy, wcscpy, _mbscpy

Copy one string to another

strcspn, wcscspn, _mbscspn,

Find first occurrence of character from specified character set in string

_strdup, _wcsdup, _mbsdup

Duplicate string

strerror, _wcserror

Map error number to message string

_strerror, __wcserror

Map user-defined error message to string

strftime, wcsftime

Format date-and-time string

_stricmp, _wcsicmp, _mbsicmp

Compare two strings without regard to case

strlen, wcslen, _mbslen, _mbstrlen

Find length of string

_strlwr, _wcslwr, _mbslwr

Convert string to lowercase

strncat, wcsncat, _mbsncat

Append characters of string

strncmp, wcsncmp, _mbsncmp

Compare characters of two strings

strncpy, wcsncpy, _mbsncpy

Copy characters of one string to another

_strnicmp, _wcsnicmp, _mbsnicmp

Compare characters of two strings without regard to case

_strnset, _wcsnset, _mbsnset

Set first n characters of string to specified character

strpbrk, wcspbrk, _mbspbrk

Find first occurrence of character from one string in another string

strrchr, wcsrchr,_mbsrchr

Find last occurrence of given character in string

_strrev, _wcsrev,_mbsrev

Reverse string

_strset, _wcsset, _mbsset

Set all characters of string to specified character

strspn, wcsspn, _mbsspn

Find first substring from one string in another string

strstr, wcsstr, _mbsstr

Find first occurrence of specified string in another string

strtok, wcstok, _mbstok

Find next token in string

_strupr, _wcsupr, _mbsupr

Convert string to uppercase

strxfrm, wcsxfrm

Transform string into collated form based on locale-specific information

vsprintf, _vstprint

Write formatted output using a pointer to a list of arguments

 

如果使用的是BSTR,那么如下的一些API需要用到:

String Manipulation Functions

Descriptions

SysAllocString

Creates and initializes a string.

SysAllocStringByteLen

Creates a zero-terminated string of a specified length (32-bit only).

SysAllocStringLen

Creates a string of a specified length.

SysFreeString

Frees a previously created string.

SysReAllocString

Changes the size and value of a string.

SysReAllocStringLen

Changes the size of an existing string.

SysStringByteLen

Returns the length of a string in bytes (32-bit only).

SysStringLen

Returns the length of a string.

CString::AllocSysString()

CString 得到 BSTR

CString::SetSysString()

重新申 BSTR ,并制到 CString

 

如果用到的是STL(标准库)中的string或者wstring,那么除了可以使用自己模板类的函数之外,还有一些STL中的强大功能供你使用,这些算法无论从通用性还是效率上,都异常强大。

下表列举算法库中的一些函数,注意,在使用之前需要包含<algorithm>

binary_search

Tests whether there is an element in a sorted range that is equal to a specified value or that is equivalent to it in a sense specified by a binary predicate.

copy

Assigns the values of elements from a source range to a destination range, iterating through the source sequence of elements and assigning them new positions in a forward direction.

count

Returns the number of elements in a range whose values match a specified value.

count_if

Returns the number of elements in a range whose values match a specified condition.

equal

Compares two ranges element by element either for equality or equivalence in a sense specified by a binary predicate.

find

Locates the position of the first occurrence of an element in a range that has a specified value.

find_first_of

Searches for the first occurrence of any of several values within a target range or for the first occurrence of any of several elements that are equivalent in a sense specified by a binary predicate to a specified set of the elements.

find_if

Locates the position of the first occurrence of an element in a range that satisfies a specified condition.

for_each

Applies a specified function object to each element in a forward order within a range and returns the function object.

includes

Tests whether one sorted range contains all the elements contained in a second sorted range, where the ordering or equivalence criterion between elements may be specified by a binary predicate.

max

Compares two objects and returns the larger of the two, where the ordering criterion may be specified by a binary predicate.

merge

Combines all the elements from two sorted source ranges into a single, sorted destination range, where the ordering criterion may be specified by a binary predicate.

min

Compares two objects and returns the lesser of the two, where the ordering criterion may be specified by a binary predicate.

partial_sort

Arranges a specified number of the smaller elements in a range into a nondescending order or according to an ordering criterion specified by a binary predicate.

remove

Eliminates a specified value from a given range without disturbing the order of the remaining elements and returning the end of a new range free of the specified value.

remove_copy

Copies elements from a source range to a destination range, except that elements of a specified value are not copied, without disturbing the order of the remaining elements and returning the end of a new destination range.

replace

Examines each element in a range and replaces it if it matches a specified value.

reverse

Reverses the order of the elements within a range.

reverse_copy

Reverses the order of the elements within a source range while copying them into a destination range

search

Searches for the first occurrence of a sequence within a target range whose elements are equal to those in a given sequence of elements or whose elements are equivalent in a sense specified by a binary predicate to the elements in the given sequence.

search_n

Searches for the first subsequence in a range that of a specified number of elements having a particular value or a relation to that value as specified by a binary predicate.

sort

Arranges the elements in a specified range into a nondescending order or according to an ordering criterion specified by a binary predicate.

stable_sort

Arranges the elements in a specified range into a nondescending order or according to an ordering criterion specified by a binary predicate and preserves the relative ordering of equivalent elements.

swap

Exchanges the values of the elements between two types of objects, assigning the contents of the first object to the second object and the contents of the second to the first.

transform

Applies a specified function object to each element in a source range or to a pair of elements from two source ranges and copies the return values of the function object into a destination range.

unique

Removes duplicate elements that are adjacent to each other in a specified range.

 

10     各种类型的字符串之间转换

常用的字符串类之间的转换方法是:将源字符串转换为C类型字符串指针,然后将该指针传递给目标类的构造函数。

 

10.1   TCHAR*转换成CString

  若将TCHAR*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:

TCHAR chArray[] = _T("This is a test");
LPTSTR p = _T("This is a test");
CString theString = chArray;
theString.Format(_T("%s"), chArray);
theString = p;

 

10.2   CString转换成TCHAR*

  方法一,使用强制转换。例如:

CString theString(_T("This is a test") );
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;

 

  方法二,使用strcpy。例如:

CString theString(_T("This is a test") );
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
_tcscpy(lpsz, theString);

 

  方法三,使用CString::GetBuffer。例如:

CString s(_T("This is a test "));
LPTSTR p = s.GetBuffer();
//
里添加使用p的代

if(p != NULL) *p = _T('/0');
s.ReleaseBuffer();

// 使用完后及时释放,以便能使用其它的CString函数

 

10.3   TCHAR*转换成BSTR

  方法一,使用SysAllocStringAPI函数。例如:

BSTR bstrText = ::SysAllocString(_T("Test"));
BSTR bstrText = ::SysAllocStringLen(_T("Test"),4);
BSTR bstrText = ::SysAllocStringByteLen(_T("Test"),4);


  方法二,使用_bstr_t,这是一种最简单的方法。例如:

BSTR bstrText = _bstr_t(_T("This is a test"));


  方法三,使用CComBSTR。例如:

BSTR bstrText = CComBSTR(_T("This is a test"));


  或

CComBSTR bstr(_T("This is a test"));
BSTR bstrText = bstr.m_str;


  方法四,使用ConvertStringToBSTR。例如:

TCHAR* lpszText = _T("Test");
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);

 

10.4   BSTR转换成TCHAR*

  方法一,使用ConvertBSTRToString。例如:

BSTR bstrText = ::SysAllocString(_T("Test"));
TCHAR* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
SysFreeString(bstrText);

// 用完
delete[] lpszText2;


  方法二,使用_bstr_t的赋值运算符重载。例如:

_bstr_t b = bstrText;
TCHAR* lpszText2 = b;

 

10.5   CString转换成BSTR

CStringAllocSysString()和SetSysString()能够从CString中得到BSTR,并在必要时转换成Unicode。除了SetSysString()使用BSTR*参数外,二者一样。他们内部调用了SysAllocString,所以由他们创建的BSTR,需要使用者显式的释放SysFreeString。

 

例如:

CString s5 = _T("Bob!");

BSTR bs1 = NULL, bs2 = NULL;

bs1 = s5.AllocSysString();

s5.SetSysString ( &bs2 );

// ...

SysFreeString ( bs1 ); // 用完

SysFreeString ( bs2 );

 

10.6   BSTR转换成CString

  一般可按下列方法进行

BSTR bstrText = ::SysAllocString(_T("Test"));
CString str = bstrText;

  或

CStringA str(bstrText);

 

10.7   basic_string转化成TCHAR*

  使用basic_string::c_str()方法转化成C风格的字符串。

 

10.8   ANSIUnicode和宽字符之间的转换

  方法一,使用MultiByteToWideChar将MBCS字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。

  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode。例如:

TCHAR tstr[] = _T("this is a test");
wchar_t wszStr[] = L"This is a test";


  方法三,使用ATL 3.0(即VC6.0ATL的转换宏和类。
ATL的字符串转换宏可以方便地转换不同编码的字符,用在函数中很有效。宏按照[source type]2[new type] 或 [source type]2C[new type]格式命名。后者转换为一个常量指针 (名字内含"C")。

 

ATL的转换宏:

A2BSTR

OLE2A

T2A

W2A

A2COLE

OLE2BSTR

T2BSTR

W2BSTR

A2CT

OLE2CA

T2CA

W2CA

A2CW

OLE2CT

T2COLE

W2COLE

A2OLE

OLE2CW

T2CW

W2CT

A2T

OLE2T

T2OLE

W2OLE

A2W

OLE2W

T2W

W2T


上表中的宏函数缩写含义如下:

2

表示转换为、转换到的含义。

A

MBCS字符串,char* (A for ANSI)

WOLE

宽字符串。也就是 UNICODEwchar_t* (W for wide)OLECHAR字符串OLECHAR* (实际等于W)

T

TCHAR字符串,TCHAR*。如果定义了_UNICODE,则T表示W;如果定义了 _MBCS,则T表示A

C

const 的缩写

BSTR

BSTR (只用于目的类型)

 

例如,W2A() 将Unicode字符串转换为MBCS字符串,T2CW()将TCHAR字符串转换为Unicode字符串常量。

要使用宏转换,程序中要包含atlconv.h头文件。可以在非ATL程序中使用宏转换,因为头文件不依赖其它的ATL,也不需要 _Module全局变量。如在函数中使用转换宏,在函数起始处先写上USES_CONVERSION宏它表明某些局部变量由宏控制使用。

转换得到的结果字符串,只要不是BSTR,都存储在堆栈中。如果要在函数外使用这些字符串,就要将这些字符串拷贝到其它的字符串类。如果结果是BSTR,内存不会自动释放,因此必须将返回值分配给一个BSTR变量或BSTR的包装类,以避免内存泄露,或者自己处理BSTR的内存

 

示例:

// 带有字符串的函数:

void Foo ( LPCWSTR wstr );

void Bar ( BSTR bstr );

// 返回字符串的函数:

void Baz ( BSTR* pbstr );

#include <atlconv.h>

 

{

using std::string;

USES_CONVERSION;    // 声明局部变量由宏控制使用

 

// 示例1:送一个MBCS字符串到Foo()

LPCSTR psz1 = "Bob";

string str1 = "Bob";

Foo ( A2CW(psz1) );

Foo ( A2CW(str1.c_str()) );

 

// 示例2:将MBCS字符串和Unicode字符串送到Bar()

LPCSTR psz2 = "Bob";

LPCWSTR wsz = L"Bob";

BSTR bs1;

CComBSTR bs2;

bs1 = A2BSTR(psz2);         // 创建 BSTR

bs2.Attach ( W2BSTR(wsz) ); // 同上,分配到CComBSTR

Bar ( bs1 );

Bar ( bs2 );

SysFreeString ( bs1 );      // 释放bs1

// 不必释放bs2,由CComBSTR释放。

 

// 示例3:转换由Baz()返回的BSTR

BSTR bs3 = NULL;

string str2;

Baz ( &bs3 );          // Baz() 填充bs3内容

str2 = W2CA(bs3);      // 转换为MBCS字符串

SysFreeString ( bs3 ); // 释放bs3

}

 

10.9   宽字符和MBCS的转化

这里所说的MBCS,实际上是指一个字符串中既含有单字节还有双字节的字符。一般来说,使用WideCharToMultiByte()MultiByteToWideChar()来进行相互的转化。

 

    1. 函数WideCharToMultiByte (),转换UNICODEMBCS。使用范例:

    LPCOLESTR lpw = L"Hello,你好";

    size_t wLen = wcslen( lpw ) + 1;  // 宽字符字符长度,+1表示包含字符串结束符

   

    int aLen=WideCharToMultiByte(  //计算所需 MBCS 字符串字节长度

                CP_ACP,

                0,

                lpw,  // 宽字符串指针

                wLen, // 字符长度

                NULL,

                0,  // 参数0表示计算转换后的字符空间

                NULL,

                NULL);

       

      LPSTR lpa = new char [aLen];

      WideCharToMultiByte(  //进行转化

                CP_ACP,

                0,

                lpw,

                wLen,

                lpa,  // 转换后的字符串指针

                aLen, // 给出空间大小

                NULL,

                NULL);

 

      // 此时,lpa 中保存着转换后的 MBCS 字符串

      ... ... ... ...

      delete [] lpa;


    2
、函数 MultiByteToWideChar(),转换 MBCS UNICODE。使用范例:

      LPCSTR lpa = "Hello,你好";

      size_t aLen = strlen( lpa ) + 1;

     

      int wLen = MultiByteToWideChar(

                CP_ACP,

                0,

                lpa,

                aLen,

                NULL,

                0);

     

      LPOLESTR lpw = new WCHAR [wLen];

      MultiByteToWideChar(

                CP_ACP,

                0,

                lpa,

                aLen,

                lpw,

                wLen);

      ... ... ... ...

      delete [] lpw;

 

关于这两个函数的第一个参数,即CP_ACP,就是要指定Code Page,可选的Code Page,比如:

Value

Meaning

CP_ACP

ANSI code page

CP_MACCP

Macintosh code page

CP_OEMCP

OEM code page

CP_SYMBOL

Windows 2000/XP: Symbol code page (42)

CP_THREAD_ACP

Windows 2000/XP: The current thread's ANSI code page

CP_UTF7

Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-7

CP_UTF8

Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-8.

其他的Code Page,在使用的时候情查询MSDNCode-Page Identifiers以获取更多和更详细的信息。

 

11     String和其他数据类型(数字类型)的转化

这里所说的其他类型主要是数字类型。

 

下表列出了一些运行时库的字符串转化处理API

Routine

Use

_ecvt

Convert double to string of specified length

_fcvt

Convert double to string with specified number of digits following decimal point

_gcvt

Convert double number to string; store string in buffer

_itot, _i64tot

Convert int to string

_ltot

Convert long to string

strtod, wcstod

Convert string to double

strtol, wcstol

Convert string to long integer

strtoul, wcstoul

Convert string to unsigned long integer

atof,_wtof

Convert string to a double

_ttoi, _ttoi64

Convert string to int or __int64

_ttol

Convert string to long

 

当然把其他类型转化成字符串,还有printf()wprintf()函数来使用。

 

有一些字符串类,如CComBSTR或者_bstr_t等,本身不提供和其他数据类型相互转化的功能(比如,format),所以只能先把字符串数据取出来,转化成C风格的字符串,然后调用运行时库的API来进行转化;或者先把其他类型数据转化成字符串类型,然后通过API来转化。

 

12     一些建议

Win32平台上开发,有一些共同的原则要遵循。

 

1.       UNICODE方式编译

应该设置为_UNICODE方式编译。设置条件编译的方式是:VC6中,Project->Settings...->C/C++ tab->Preprocessor definitions中把_MBCS修改为_UNICODE,UNICODE。同时,在ProjectSetting/link/output 中设置EntrywWinMainCRTStartup

注意:_UNICODEUNICODE是不同的。_UNICODE宏用于C运行期头文件,UNICODE宏则用于Windows头文件。但是一般我们并不区分这种区别,而是笼统的把这两个宏都定义上。

如果用的是UNICODE字符,调试中经常发现只能看到第一个字符的问题,可以作如下设置就可以看到全部的字符串了:

Tools->Options...->Debug tab->Display Unicode strings

打上勾就好了。

 

2.       使用T类型

在实际编码中,必须使用T类型,这是非常好的习惯,必须遵守

 
原创粉丝点击