Chinaunix首页 | 论坛 | 博客
  • 博客访问: 660435
  • 博文数量: 151
  • 博客积分: 3498
  • 博客等级: 中校
  • 技术积分: 1570
  • 用 户 组: 普通用户
  • 注册时间: 2005-02-28 18:10
文章分类

全部博文(151)

文章存档

2014年(12)

2013年(17)

2012年(17)

2011年(5)

2010年(12)

2009年(2)

2007年(26)

2006年(22)

2005年(38)

分类: LINUX

2007-11-11 09:26:15

哪里出错了呢?看看上面两个被用蓝色高量显示的字节。斜杠\\的值是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.
    pLastChar = 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()将会返回6Unicode函数返回的长度是按wchar_t来计算的。例如,wcslen(LBob)返回3

Win32 API中的MBCSUnicode

两组 APIs
  尽管你也许从来没有注意过,Win32中的每个与字符串相关的APImessage都有两个版本。一个版本接受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 APIsbuild时,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 );

 

在预处理器把SetWindowTextSetWindowTextW来替换后,代码变成:

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是一种字符串类型,它让你在以MBCSUNNICODEbuild时可以使用同样的代码,不需要使用繁琐的宏定义来包含你的代码。TCHAR的定义如下:

#ifdef UNICODEtypedef wchar_t TCHAR;#elsetypedef char TCHAR;#endif

所以用MBCSbuild时,TCHARchar,使用UNICODE时,TCHARwchar_t。还有一个宏来处理定义Unicode字符串常量时所需的L前缀。

#ifdef UNICODE#define _T(x) L##x#else#define _T(x) x#endif

  ##是一个预处理操作符,它可以把两个参数连在一起。如果你的代码中需要字符串常量,在它前面加上_T宏。如果你使用Unicodebuild,它会在字符串常量前加上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())。 MSDNGeneric-Text Routine Mappings.标题下有完整的宏列表。
字符串和TCHAR typedefs

  由于Win32 API文档的函数列表使用函数的常用名字(例如,SetWindowText),所有的字符串都是用TCHAR来定义的。(除了XP中引入的只适用于UnicodeAPI)。下面列出一些常用的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 APINT允许使用非常长的文件名(突破了MAX_PATH的限制,MAX_PATH=260)。使用Unicode API的另一个优点是你的会自动处理用户输入的各种语言。所以一个用户可以输入英文,中文或者日文,而你不需要额外编写代码去处理它们。
  最后,随着windows 9x产品的淡出,微软似乎正在抛弃MBCS APIs。例如,包含两个字符串参数的SetWindowTheme() API只有Unicode版本的。使用Unicodebuild你的将会简化字符串的处理,你不必在MBCSUnicdoe之间相互转换。
  即使你现在不使用Unicodebuild你的,你也应该使用TCHAR及其相关的宏。这样做不仅可以的代码可以很好地处理DBCS,而且如果将来你想用Unicodebuild你的,你只需要改变一下预处理器中的设置就可以实现。



对各字符集编码范围的总结


[精华] [分享]对各字符集编码范围的总结[更新日期2007-03-12]


作者:  发表于:2007-04-09 11:26:30
【】 【】 【】【】

最近项目中用到了对文字、标点以及特殊字符的判断。
网上关于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全角片假名 [color=Red]2007-03-12 15:00更新[/color]
\xa3[\xb0-\xb9]|\xa3[\xc1-\xda]|\xa5[\xa1-\xf6][\xa3][\xb0-\xfa]|[\xa1][\xbc-\xbe]|[\xa1][\xdd]

EUC_JP全角汉字 [color=Red]2007-03-12 15:06更新[/color]
[\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]


[color=Red]2007-03-12 21:35 补充[/color]
日文半角空格
\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])


阅读(1136) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~