1,在VS2010 编译器的菜单上 “项目----属性---左侧的配置属性----常规---右侧的 字符集 ” 可以对项目所要使用的字符集进行定义,可以选择”使用Unicode字符集“、“使用多字节字符集”等等。
2,MessageBox 有三个版本:MessageBoxA, MessageBoxW, MessageBox;
****************************************
WINUSERAPI
int
WINAPI
MessageBoxA(
__in_opt HWND hWnd,
__in_opt LPCSTR lpText,
__in_opt LPCSTR lpCaption,
__in UINT uType);
WINUSERAPI
int
WINAPI
MessageBoxW(
__in_opt HWND hWnd,
__in_opt LPCWSTR lpText,
__in_opt LPCWSTR lpCaption,
__in UINT uType);
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif // !UNICODE
**********************************************
即:如果项目设置使用UNICODE字符集, MessageBox 就相当于 MessageBoxW, 否则 MessageBox 就相当于 MessageBoxA;
在MessageBoxA函数的第二和第三个参数的类型都是LPCSTR ,它是什么呢?
typedef __nullterminated CONST CHAR *LPCSTR, *PCSTR;
它是 CONST CHAR, CONST CHAR 是什么?
#ifndef CONST
#define CONST const
#endif
typedef char CHAR;
所以 CONST CHAR 就相当于 const char; 所以MessageBoxA 中的第二和第三个参数要是多字节(ANSI)类型常量的。
在MessageBoxW函数的第二和第三个参数的类型都是LPCWSTR ,它是什么呢?
typedef __nullterminated CONST WCHAR *LPCWSTR, *PCWSTR;
它是 CONST WCHAR; CONST WCHAR又是什么?
#ifndef CONST
#define CONST const
#endif
#ifndef _MAC
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
#else
// some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char
typedef unsigned short WCHAR; // wc, 16-bit UNICODE character
#endif
所以CONST WCHAR 就相当于 const wchar_t ,所以MessageBoxW 中的第二和第三个参数要是16位的UNICODE 常量。
3,CString 也有三个版本,CStringA, CStringW, CString;
**************************************************
typedef ATL::CStringT< wchar_t, StrTraitMFC_DLL< wchar_t > > CStringW;
typedef ATL::CStringT< char, StrTraitMFC_DLL< char > > CStringA;
typedef ATL::CStringT< TCHAR, StrTraitMFC_DLL< TCHAR > > CString;
******************************************************************
4,上面CString 模板中的 TCHAR 是什么?
*****************************************************************
//
// Neutral ANSI/UNICODE types and macros
//
#ifdef UNICODE // r_winnt
#ifndef _TCHAR_DEFINED
typedef WCHAR TCHAR, *PTCHAR;
typedef WCHAR TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */
/*........(此处省略多条定义)*/
#else /* UNICODE */ // r_winnt
#ifndef _TCHAR_DEFINED
typedef char TCHAR, *PTCHAR;
typedef unsigned char TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */
即:如果项目设置使用UNICODE字符集 ,TCHAR 就相当于 WCHAR,也就是wchar_t;否则 TCHAR 就相当于 char;所以项目设置使用UNICODE字符集, CString就相当于CStringW, 否则CString 就相当于CStringA.
如果在配置中设置了使用UNICODE字符集,那么CString 的作用就如同CStringW一样,如果把光标移动到程序中的CString上面,就会显示它的第一个模板参数是wchar_t。这时如果修改字符集使用多字节字符,CString的作用就如同CStringA一样了,如果把光标移动到它上面,会显示它的第一个模板参数是char。
5 在给宽字节变量赋值时,可以在值前加“L”,也可以加“_T”,比如:
wstring x(L"你好");
CStringW y(L"再见");
wstring x(_T("你好") );
CStringW y(_T("再见") );
在值前加“L”是常见用法,那么“_T”表示什么呢?
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
那么 __T(x) 是什么?
#ifdef UNICODE // r_winnt
/*........(此处省略多条定义)*/
#define __TEXT(quote) L##quote // r_winnt
#else /* UNICODE */ // r_winnt
/*........(此处省略多条定义)*/
#define __TEXT(quote) quote // r_winnt
#endif /* UNICODE */ // r_winnt
即:如果项目设置使用UNICODE字符集, “_T” 就相当于“L”,否则加“_T”就如同没加。
6 ANSI 与 UNICODE 的混合编程。编程时,使用ANSI节省存储空间,但遇到处理中文时很不方便。如何使所编写的一个程序在处理英文时,使用ANSI字符集,在处理中文时,使用UNICODE字符集,并且不需要修改大量代码。
a, 使用CString 类型的字符串变量;
b, 使用MessageBox 来弹出消息;
c, 使用TCHAR类型的字符变量;
d, 使用_T来表示宽字节字符。
在项目设置使用UNICODE字符集时,CString 就相当于CStringW, MessageBox就相当于MessageBoxW,TCHAR 就相当于wchar_t, _T就相当于L;
在项目设置使用多字节字符集时,CString 就相当于CStringA, MessageBox就相当于MessageBoxA,TCHAR就相当于char, _T就相当于不存在。
比如一条语句:
CString x( _T("大家好") );
在项目设置使用UNICODE字符集时,CString 就相当于 CStringW, x 中存储的字符长度为3,_T就相当于L;在项目设置使用多字节字符集时,CString 就相当于CStringA, x 中存储的字符长度为6。
而string 和 wstring 却没有类似的转化功能。
typedef basic_string<char, char_traits<char>, allocator<char> > string;
typedef basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> > wstring
当然如果处于某种考虑,某个变量就要存储 宽字节字符 ,而不是存储ANSI字符,就可以显式的使用CStringW类型,而不是使用CString。
二、分析一个文本断句的函数用CStringW的函数和正则表达式实现文本断句
文本断句函数是在文本信息处理中经常用到,并且如果对这个函数做适当的修改就可以用于文本块的切割。
我以前编写过一个这样的函数,在ANSI字符集下编程,
为了判断一个字符中文标点和英文标点,定义了char类型的变量来存储一个字节,但是这要会出现一个问题:在某些实现中char 会被当作signed类型,在另外一些实现中被当作 unsigned 类型,因此在不同的编译器中需要修改代码。比如,在我用的VC6.0中,判断一个字节是不是英文标点用 if(a>0),而到了vc2010,就要用if(a<129)。另外在ANSI字符集下编程断句还容易出现字符错位问题。
最近又编写了一个文本断句函数,使用UNICODE字符集,用较少的代码,实现了功能:
///
//function: cut sentence to blocks;
//input: csLine, mark;
//output:csLine after cut;
void cutSentenceAtUnicode( CStringW& csLine, const CStringW mark )
{
for( int i = 0; i < ( csLine.GetLength() - 1 ); ++i )
if ( ( ( csLine.Mid(i,1) ).FindOneOf( mark ) >= 0 ) &&
( ( csLine.Mid( i + 1 ,1) ).FindOneOf( mark ) < 0 ) )
csLine.Insert(i+1, _T("\n") );
csLine = csLine + L"\n";
}
注释: csLine 是需要断句的字符串, mark是切分的标志,比如可以定义为:
CStringW mark(L".?!\"。,?!”")
可以同时在里面包含中文标点和英文标点,以及句末标点和右匹配标点。都会把它当作宽字符来处理。
思路很简单,就是:如果字符串的第i处字符包含在切分标志中,而第i+1处字符并不包含在切分标志中,就在第i处之后添加一个回车符。
这个函数可以处理UNICODE字符,当然也可以把ANSI的转化为UNICODE来断句。
// 用正则表达式来断句 (输入和输出均为 CStringW类型);
void cutSentenceAtUnicodeReg( CStringW& csLine )
{
wregex reg( _T("([.?!\"。,?!”])(?![ .?!\"。,?!”])") );
wstring wstr = csLine;
wstring wfmt(_T("$1\n"));
csLine = ( regex_replace(wstr, reg, wfmt)).c_str();
}
// 用正则表达式来断句 (输入和输出均为 wstring 类型);
void cutSentenceAtUnicodeReg( wstring& csLine )
{
wregex reg( _T("([.?!\"。,?!”])(?![ .?!\"。,?!”])") );
wstring wfmt(_T("$1\n"));
csLine = regex_replace(csLine, reg, wfmt) ;
}
正则表达式“([.?!\"。,?!”])(?![ .?!\"。,?!”])”有两个组,第一组是“([.?!\"。,?!”])”、第二组是“(?![.?!\"。,?!”])”,与第一组匹配的字符串会被保存到缓冲区1中,第二组的作用是“否定式向前查找”,它的模式是(?!...); 就是当在第一组之后,没有出现第二组中的内容才符合匹配的条件;如果在第一组后出现了第二组的内容就不符合匹配的条件。所以这个表达式所表达的思想与第一种文本切分的方法是一致的,即:如果字符串的第i处字符包含在切分标志中,而第i+1处字符并不包含在切分标志中,就在第i处之后添加一个回车符。