- 论坛徽章:
- 0
|
哪里出错了呢?看看上面两个被用蓝色高量显示的字节。斜杠\\的值是0x5c。 的值是83 5c。上面的代码错误的读取了一个 trail byte,把它当作了一个字符。
正确的后向遍历方法是使用能够识别DBCS字符的函数,使指针移动正确的字节数。下面是正确的代码。(指针移动的地方用红色标明)
![]()
bool FixedGetConfigFileName( char* pszName, size_t nBuffSize )
![]()
![]()
...{
![]()
char szConfigFilename[MAX_PATH];
![]()
![]()
// Read install dir from registry... well assume it succeeds.
![]()
// Add on a backslash if it wasnt present in the registry value.
![]()
// First, get a pointer to the terminating zero.
![]()
char* pLastChar = _mbschr ( szConfigFilename, 0 );
![]()
![]()
// Now move it back one double-byte character.
![]()
[color="#ff0000"]pLastChar [color="#ff0000"]=[color="#ff0000"] CharPrev ( szConfigFilename, pLastChar );
![]()
![]()
if ( *pLastChar != \ ) _mbscat ( szConfigFilename, \ );
![]()
![]()
// Add on the name of the config file.
![]()
_mbscat ( szConfigFilename, config.bin );
![]()
![]()
// If the callers buffer is big enough, return the filename.
![]()
if ( _mbslen ( szInstallDir ) >= nBuffSize ) return false;
![]()
else
![]()
![]()
...{
![]()
_mbscpy ( pszName, szConfigFilename );
![]()
return true;
![]()
}
![]()
}
上面的函数使用CharPrev() API使pLastChar向后移动一个字符,这个字符可能是两个字节长。在这个版本里,if条件正常工作,因为lead byte永远不会等于0x5c。
让我们来想象一个违背规则1的场合。例如,你可能要检测一个用户输入的文件名是否多次出现了:。如果,你使用++操作来遍历字符串,而不是使用CharNext(),你可能会发出不正确的错误警告如果恰巧有一个trail byte它的值的等于:的值。
与规则2相关的关于字符串索引的规则:
2a. 永远不要使用减法去得到一个字符串的索引。
违背这条规则的代码和违背规则2的代码很相似。例如,
char* pLastChar = &szConfigFilename [strlen(szConfigFilename) - 1];
这和向后移动一个指针是同样的效果。
回到关于str***()和_mbs***()的区别
现在,我们应该很清楚为什么_mbs***()函数是必需的。Str***()函数根本不考虑DBCS字符,而_mbs***()考虑。如果,你调用strrchr(C:\\ , \\),返回结果可能是错误的,然而_mbsrchr()将会认出最后的双字节字符,返回一个指向真的\\的指针。
关于字符串函数的最后一点:str***()和_mbs***()函数认为字符串的长度都是以char来计算的。所以,如果一个字符串包含3个双字节字符,_mbslen()将会返回6。Unicode函数返回的长度是按wchar_t来计算的。例如,wcslen(LBob)返回3。Win32 API中的MBCS和Unicode
两组 APIs:
尽管你也许从来没有注意过,Win32中的每个与字符串相关的API和message都有两个版本。一个版本接受MBCS字符串,另一个接受Unicode字符串。例如,根本没有SetWindowText()这个API,相反,有SetWindowTextA()和SetWindowTextW()。后缀A表明这是MBCS函数,后缀W表示这是Unicode版本的函数。
当你 build 一个 Windows
程序
,你可以选择是用 MBCS 或者 Unicode APIs。如果,你曾经用过VC向导并且没有改过预处理的设置,那表明你用的是MBCS版本。那么,既然没有 SetWindowText() API,我们为什么可以使用它呢?winuser.h头文件包含了一些宏,例如:
![]()
BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
![]()
BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );
![]()
![]()
#ifdef UNICODE
![]()
#define SetWindowText SetWindowTextW
![]()
#else
![]()
#define SetWindowText SetWindowTextA
![]()
#endif
当使用MBCS APIs来build
程序
时,UNICODE没有被定义,所以预处理器看到:
#define SetWindowText SetWindowTextA
这个宏定义把所有对SetWindowText的调用都转换成真正的API函数SetWindowTextA。(当然,你可以直接调用SetWindowTextA() 或者 SetWindowTextW(),虽然你不必那么做。)
所以,如果你想把默认使用的API函数变成Unicode版的,你可以在预处理器设置中,把_MBCS从预定义的宏列表中删除,然后添加UNICODE和_UNICODE。(你需要两个都定义,因为不同的头文件可能使用不同的宏。) 然而,如果你用char来定义你的字符串,你将会陷入一个尴尬的境地。考虑下面的代码:
![]()
HWND hwnd = GetSomeWindowHandle();
![]()
char szNewText[] = we love Bob!;
![]()
SetWindowText ( hwnd, szNewText );
![]()
在预处理器把SetWindowText用SetWindowTextW来替换后,代码变成:
![]()
HWND hwnd = GetSomeWindowHandle();
![]()
char szNewText[] = we love Bob!;
![]()
SetWindowTextW ( hwnd, szNewText );
看到问题了吗?我们把单字节字符串传给了一个以Unicode字符串做参数的函数。解决这个问题的第一个方案是使用 #ifdef 来包含字符串变量的定义:
你可能已经感受到了这样做将会使你多么的头疼。完美的解决方案是使用TCHAR.
![]()
HWND hwnd = GetSomeWindowHandle();
![]()
![]()
#ifdef UNICOD
![]()
Ewchar_t szNewText[] = Lwe love Bob!;
![]()
#else
![]()
char szNewText[] = we love Bob!;
![]()
#endif
![]()
![]()
SetWindowText ( hwnd, szNewText );
使用TCHAR
TCHAR是一种字符串类型,它让你在以MBCS和UNNICODE来build
程序
时可以使用同样的代码,不需要使用繁琐的宏定义来包含你的代码。TCHAR的定义如下:
#ifdef UNICODEtypedef wchar_t TCHAR;#elsetypedef char TCHAR;#endif
所以用MBCS来build时,TCHAR是char,使用UNICODE时,TCHAR是wchar_t。还有一个宏来处理定义Unicode字符串常量时所需的L前缀。
#ifdef UNICODE#define _T(x) L##x#else#define _T(x) x#endif
##是一个预处理操作符,它可以把两个参数连在一起。如果你的代码中需要字符串常量,在它前面加上_T宏。如果你使用Unicode来build,它会在字符串常量前加上L前缀。
TCHAR szNewText[] = _T(we love Bob!);
像是用宏来隐藏SetWindowTextA/W的细节一样,还有很多可以供你使用的宏来实现str***()和_mbs***()等字符串函数。例如,你可以使用_tcsrchr宏来替换strrchr()、_mbsrchr()和wcsrchr()。_tcsrchr根据你预定义的宏是_MBCS还是UNICODE来扩展成正确的函数,就像SetWindowText所作的一样。
不仅str***()函数有TCHAR宏。其他的函数如, _stprintf(代替sprinft()和swprintf()),_tfopen(代替fopen()和_wfopen())。 MSDN中Generic-Text Routine Mappings.标题下有完整的宏列表。
字符串和TCHAR typedefs
由于Win32 API文档的函数列表使用函数的常用名字(例如,SetWindowText),所有的字符串都是用TCHAR来定义的。(除了XP中引入的只适用于Unicode的API)。下面列出一些常用的typedefs,你可以在msdn中看到他们。
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*)
何时使用 TCHAR 和 Unicode
到现在,你可能会问,我们为什么要使用Unicode。我已经用了很多年的char。下列3种情况下,使用Unicode将会使你受益:
· 1.你的
程序
只运行在Windows NT
系统
中。
· 2. 你的
程序
需要处理超过MAX_PATH个字符长的文件名。
· 3. 你的
程序
需要使用XP中引入的只有Unicode版本的API.
Windows 9x 中大多数的 API 没有实现 Unicode 版本。所以,如果你的
程序
要在windows 9x中运行,你必须使用MBCS APIs。然而,由于NT
系统
内部都使用Unicode,所以使用Unicode APIs将会加快你的
程序
的运行速度。每次,你传递一个字符串调用MBCS API,操作
系统
会把这个字符串转换成Unicode字符串,然后调用对应的Unicode API。如果一个字符串被返回,操作
系统
还要把它转变回去。尽管这个转换过程被高度优化了,但它对速度造成的损失是无法避免的。
只要你使用Unicode API,NT
系统
允许使用非常长的文件名(突破了MAX_PATH的限制,MAX_PATH=260)。使用Unicode API的另一个优点是你的
程序
会自动处理用户输入的各种语言。所以一个用户可以输入英文,中文或者日文,而你不需要额外编写代码去处理它们。
最后,随着windows 9x产品的淡出,微软似乎正在抛弃MBCS APIs。例如,包含两个字符串参数的SetWindowTheme() API只有Unicode版本的。使用Unicode来build你的
程序
将会简化字符串的处理,你不必在MBCS和Unicdoe之间相互转换。
即使你现在不使用Unicode来build你的
程序
,你也应该使用TCHAR及其相关的宏。这样做不仅可以的代码可以很好地处理DBCS,而且如果将来你想用Unicode来build你的
程序
,你只需要改变一下预处理器中的设置就可以实现。
[color="#05006c"]对各字符集编码范围的总结
http://www.chinaunix.net/jh/25/907172.html
[color="#05006c"][精华] [分享]对各字符集编码范围的总结[更新日期2007-03-12]
http://www.chinaunix.net 作者:
gsging
发表于:2007-04-09 11:26:30
【
发表评论
】
【
查看原文
】
【
Perl讨论区
】【
关闭
】
最近项目中用到了对文字、标点以及特殊字符的判断。
网上关于GBK、GB2312和BIG5编码范围的资料比较多,但是日文的资料比较少,我总结了一下,希望能对大家在正则中判断
这些字符集尤其是日文字符集的各种字、标点以及特殊符号的时候有所帮助。
UTF8 [\x01-\x7f]|[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}
UTF16 [\x00-\xd7][\xe0-\xff]|[\xd8-\xdf][\x00-\xff]{2}
JIS [\x20-\x7e]|[\x21-\x5f]|[\x21-\x7e]{2}
SJIS [\x20-\x7e]|[\xa1-\xdf]|([\x81-\x9f]|[\xe0-\xef])([\x40-\x7e]|[\x80-\xfc])
EUC_JP [\x20-\x7e]|\x81[\xa1-\xdf]|[\xa1-\xfe][\xa1-\xfe]|\x8f[\xa1-\xfe]{2}
EUC_JP标点符号及特殊字符 [\xa1-\xa2][\xa0-\xfe]
EUC_JP全角数字 \xa3[\xb0-\xb9]
EUC_JP全角大写英文 \xa3[\xc1-\xda]
EUC_JP全角小写英文 \xa3[\xe1-\xfa]
EUC_JP全角平假名 \xa4[\xa1-\xf3]
EUC_JP全角片假名 2007-03-12 15:00更新 \xa3[\xb0-\xb9]|\xa3[\xc1-\xda]|\xa5[\xa1-\xf6][\xa3][\xb0-\xfa]|[\xa1][\xbc-\xbe]|[\xa1][\xdd]
EUC_JP全角汉字 2007-03-12 15:06更新[\xb0-\xcf][\xa0-\xd3]|[\xd0-\xf4][\xa0-\xfe]|[\xB0-\xF3][\xA1-\xFE]|[\xF4][\xA1-\xA6]|[\xA4][\xA1-\xF3]|[\xA5][\xA1-\xF6]|[\xA1][\xBC-\xBE]
Big5 [\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|[\xa1-\xfe])
GBK [\x01-\x7f]|[\x81-\xfe][\x40-\xfe]
GB2312汉字[\xb0-\xf7][\xa0-\xfe]
GB2312半角标点符号及特殊符号 \xa1[\xa2-\xfe]
GB2312罗马数组及项目序号 \xa2([\xa1-\xaa]|[\xb1-\xbf]|[\xc0-\xdf]|[\xe0-\xe2]|[\xe5-\xee]|[\xf1-\xfc])
GB2312全角标点及全角字母 \xa3[\xa1-\xfe]
GB2312日文平假名 \xa4[\xa1-\xf3]
GB2312日文片假名 \xa5[\xa1-\xf6]
補充:
GB18030[\x00-\x7f]|[\x81-\xfe][\x40-\xfe]|[\x81-\xfe][\x30-\x39][\x81-\xfe][\x30-\x39]
2007-03-12 21:35 补充
日文半角空格 \x20
SJIS全角空格 (?:\x81\x81)
SJIS全角数字 (?:\x82[\x4f-\x58])
SJIS全角大写英文 (?:\x82[\x60-\x79])
SJIS全角小写英文 (?:\x82[\x81-\x9a])
SJIS全角平假名 (?:\x82[\x9f-\xf1])
SJIS全角平假名扩展 (?:\x82[\x9f-\xf1]|\x81[\x4a\x4b\x54\x55])
SJIS全角片假名 (?:\x83[\x40-\x96])
SJIS全角片假名扩展 (?:\x83[\x40-\x96]|\x81[\x45\x5b\x52\x53])
EUC_JP全角空格 (?:\xa1\xa1)
EUC半角片假名 (?:\x8e[\xa6-\xdf])
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/4591/showart_419522.html |
|