【转载】isspace函数的debug版本对中文处理有问题
2010-10-27 10:11
232 查看
From:http://www.cppblog.com/luonjtu/archive/2009/03/12/76332.html
vc 2005 sp1下isspace函数的debug版本对中文处理有问题
今天碰到一个怪问题,从别人那儿拿来的一段代码先在gcc下过了,又移植到vc下编译,结果debug时老是有assert错误。看了一下代码,错误发生在一个trim函数中。trim函数接受一个char*类型的字符串参数,去掉字符串前后的空格、制表符等空白字符。其中判断是否是空白字符用的是isspace函数。按照一般的想法,char*字符串里的字符编码无论是GBK还是utf-8,因为都兼容ASCII,所以isspace函数都不应该发生问题。但事实是只要是字符串有中文,无论是gbk还是utf-8编码,isspace内都有assert错误。为了便于说明,把其中的代码抽像出来如下:
char* lpszBuffer = (char*)"高";
int nLen = (int)strlen(lpszBuffer);
for( int i=0;i
{
printf("0x%x %dn",lpszBuffer[i],isspace(lpszBuffer[i]) );
}
这段代码在debug编译的情况下会assert失败。没有办法,只好跟踪到c运行库里,isspace的实现如下(在"_ctype.c"文件里):
extern __inline int (__cdecl isspace) (
int c
)
{
if (__locale_changed == 0)
{
return __fast_ch_check(c, _SPACE);
}
else
{
return (_isspace_l)(c, NULL);
}
}
跟踪发现,__locale_changed的值为0,走第一个分支,调用__fast_ch_check,它其实是个宏定义,最后进入_chvalidator函数,它的实现代码如下:
extern "C" int __cdecl _chvalidator(
int c,
int mask
)
{
_ASSERTE((unsigned)(c + 1) <= 256);
return _chvalidator_l(NULL, c, mask);
}
错误就发生在这个函数的第一行“ _ASSERTE((unsigned)(c + 1) <= 256);”。
代码看到这里,错误的原因基本也就出来了。“高”的GBK编码是“b8 df”,调用isspace函数是逐个字节判断,但是isspace和_chvalidator接受的参数都是int,这样就会产生一个char到int的转型,在vc下,char默认是"signed char",这样char“b8”转型到int后,会变成一个负数,然后在assert的时候,又强制转型为unsigned,它又变成了一个非常巨大的正数,自然assert就失败了。
为什么在gcc下没有错误?gcc下的isspace函数实现我倒没看,不过我的gcc中的char默认就是“unsighed char”,所以即使isspace的实现跟上面的一样,也不会产生问题。
另:在win32 release和wince下,也不会有上述问题,因为这几种环境下isspace的实现跟上面不一样。
问题找到了,剩下的就看怎么解决了。
首先用用一个最简单的方法,既然问题发生在转型上,只要把char的默认类型改为unsigned char就可以了,vc也提供了这个选项。但这有几个问题,一是别人用我的代码的时候还得修改编译选项,二是修改了整个工程的编译选项可能会对其它代码产生不好的影响。
既然上面的方法不大好操作,那就再想想别的方法。经观察发现,isspace函数中靠__locale_changed变量控制流程走向,搜索整个crt的源代码,发现__locale_changed的值只有在setlocale函数中发生了改变。最后,我把代码修改成如下:
#if defined(WIN32) && defined(_DEBUG)
char* locale = setlocale( LC_ALL, ".OCP" );
#endif
char* lpszBuffer = (char*)"高";
int nLen = (int)strlen(lpszBuffer);
for( int i=0;i
{
printf("0x%x %dn",lpszBuffer[i],isspace(lpszBuffer[i]) );
}
这样在debug版本上就不会再有问题,但是可能其中仍有隐患,等到具体碰到的时候再说吧。先把目前的工作继续下去。
犀利评论:
如果我没有理解错,你试图用locale为ASCII的isspace来判断GBK编码的空格,对么?如果我理解正确的话,那么这不是VC的问题,而是使用上的问题。
对于C++而言,应该使用isspace(ch, loc); 这个版本,loc是类型为std::locale的变量,如果你想判断GBK的空格,那么让loc是GBK的locale,然后这个函数就正常了。
你现在使用的是C的isspace(ch)函数,这个函数使用的是默认的全局locale,你把这个全局的设为GBK,也应该可以解决这个问题。总之调用locale为默认的ASCII的locale的isspace去判断编码为GBK的字串是否是空格,逻辑上不对。
vc 2005 sp1下isspace函数的debug版本对中文处理有问题
今天碰到一个怪问题,从别人那儿拿来的一段代码先在gcc下过了,又移植到vc下编译,结果debug时老是有assert错误。看了一下代码,错误发生在一个trim函数中。trim函数接受一个char*类型的字符串参数,去掉字符串前后的空格、制表符等空白字符。其中判断是否是空白字符用的是isspace函数。按照一般的想法,char*字符串里的字符编码无论是GBK还是utf-8,因为都兼容ASCII,所以isspace函数都不应该发生问题。但事实是只要是字符串有中文,无论是gbk还是utf-8编码,isspace内都有assert错误。为了便于说明,把其中的代码抽像出来如下:
char* lpszBuffer = (char*)"高";
int nLen = (int)strlen(lpszBuffer);
for( int i=0;i
{
printf("0x%x %dn",lpszBuffer[i],isspace(lpszBuffer[i]) );
}
这段代码在debug编译的情况下会assert失败。没有办法,只好跟踪到c运行库里,isspace的实现如下(在"_ctype.c"文件里):
extern __inline int (__cdecl isspace) (
int c
)
{
if (__locale_changed == 0)
{
return __fast_ch_check(c, _SPACE);
}
else
{
return (_isspace_l)(c, NULL);
}
}
跟踪发现,__locale_changed的值为0,走第一个分支,调用__fast_ch_check,它其实是个宏定义,最后进入_chvalidator函数,它的实现代码如下:
extern "C" int __cdecl _chvalidator(
int c,
int mask
)
{
_ASSERTE((unsigned)(c + 1) <= 256);
return _chvalidator_l(NULL, c, mask);
}
错误就发生在这个函数的第一行“ _ASSERTE((unsigned)(c + 1) <= 256);”。
代码看到这里,错误的原因基本也就出来了。“高”的GBK编码是“b8 df”,调用isspace函数是逐个字节判断,但是isspace和_chvalidator接受的参数都是int,这样就会产生一个char到int的转型,在vc下,char默认是"signed char",这样char“b8”转型到int后,会变成一个负数,然后在assert的时候,又强制转型为unsigned,它又变成了一个非常巨大的正数,自然assert就失败了。
为什么在gcc下没有错误?gcc下的isspace函数实现我倒没看,不过我的gcc中的char默认就是“unsighed char”,所以即使isspace的实现跟上面的一样,也不会产生问题。
另:在win32 release和wince下,也不会有上述问题,因为这几种环境下isspace的实现跟上面不一样。
问题找到了,剩下的就看怎么解决了。
首先用用一个最简单的方法,既然问题发生在转型上,只要把char的默认类型改为unsigned char就可以了,vc也提供了这个选项。但这有几个问题,一是别人用我的代码的时候还得修改编译选项,二是修改了整个工程的编译选项可能会对其它代码产生不好的影响。
既然上面的方法不大好操作,那就再想想别的方法。经观察发现,isspace函数中靠__locale_changed变量控制流程走向,搜索整个crt的源代码,发现__locale_changed的值只有在setlocale函数中发生了改变。最后,我把代码修改成如下:
#if defined(WIN32) && defined(_DEBUG)
char* locale = setlocale( LC_ALL, ".OCP" );
#endif
char* lpszBuffer = (char*)"高";
int nLen = (int)strlen(lpszBuffer);
for( int i=0;i
{
printf("0x%x %dn",lpszBuffer[i],isspace(lpszBuffer[i]) );
}
这样在debug版本上就不会再有问题,但是可能其中仍有隐患,等到具体碰到的时候再说吧。先把目前的工作继续下去。
犀利评论:
如果我没有理解错,你试图用locale为ASCII的isspace来判断GBK编码的空格,对么?如果我理解正确的话,那么这不是VC的问题,而是使用上的问题。
对于C++而言,应该使用isspace(ch, loc); 这个版本,loc是类型为std::locale的变量,如果你想判断GBK的空格,那么让loc是GBK的locale,然后这个函数就正常了。
你现在使用的是C的isspace(ch)函数,这个函数使用的是默认的全局locale,你把这个全局的设为GBK,也应该可以解决这个问题。总之调用locale为默认的ASCII的locale的isspace去判断编码为GBK的字串是否是空格,逻辑上不对。
相关文章推荐
- isspace函数的debug版本处理中文程序异常
- [转载]关于 Debug 和 Release 版本区别
- C#项目中的bin目录和obj目录的区别,以及Debug版本和Release版本的区别(转载)
- 【转载】Python Unicode与中文处理
- xajax addAlert 处理中文有问题
- JavaMail中文附件处理(转载)
- 「转载」 Debug Android and Linux suspend and resume (中文)
- perl对中文的处理(encode,decode) (转载)
- (转载csdn)Tomcat的中文处理(一,二)
- 过滤器处理Struts2中文乱码的问题(struts2.1.3以后版本)
- 【转载】JMeter3.0图形化HTML报告中文乱码问题处理
- Perl对中文的处理问题 (转载)
- PHP json_encode 中文不转码,低版本处理
- 钻井及完井工程设计模拟Hydraulic UnderBalanced Simulator(HUBS) R3.2.1+Forward v2.7成象测井处理解释软件 中文版本
- 转载:dota6.70改动【版本】掌门人独家翻译:DotA 6.70中文改动日志
- DEBUG和RELEASE 版本差异及调试相关问题 (转载)
- IMF spark 读入文件中文编码转换处理(JAVA版本)
- 转载的关于文件处理的中文说明
- [转载]Visual Studio中的debug和release版本的区别
- Java连接MySQL中文乱码处理【转载】