isspace函数的debug版本处理中文程序异常
2017-09-22 16:34
405 查看
开发环境 :VS2008
示例代码:
#include <iostream>
#include <string>
#include <fstream>
#include <algorithm>
#include <functional>
#include <locale>
#include <cctype>
using namespace std;
inline string& ltrim(string &str) {
string::iterator p = find_if(str.begin(), str.end(), std::not1(ptr_fun<int, int>(isspace)));
str.erase(str.begin(), p);
return str;
}
inline string& rtrim(string &str) {
string::reverse_iterator p = find_if(str.rbegin(), str.rend(), std::not1(ptr_fun<int , int>(isspace)));
str.erase(p.base(), str.end());
return str;
}
inline string& trim(string &str) {
ltrim(rtrim(str));
return str;
}
int main(){
#if _DEBUG
setlocale(LC_ALL, "chs");
#endif
string str = "\t\r\n 123sggdery中 国455 68 \r\n";
string str1 = str;
string str2 = str;
cout << "str: ~" << str << "~" << endl << endl;
cout << "ltrim(str): ~" << ltrim(str1) << "~" << endl;
cout << "rtrim(ltrim(str)): ~" << rtrim(str1) << "~" << endl << endl;
cout << "rtrim(str): ~" << rtrim(str2) << "~" << endl;
cout << "ltrim(rtrim(str)): ~" << ltrim(str2) << "~" << endl << endl;
cout << "trim(str): ~" << trim(str) << "~" << endl;
return 0;
}
上述 代码(没有设置 setlocale 的时候 )在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函数中发生了改变。最后,我把代码进行修改 添加了 setlocale 解决此问题
示例代码:
#include <iostream>
#include <string>
#include <fstream>
#include <algorithm>
#include <functional>
#include <locale>
#include <cctype>
using namespace std;
inline string& ltrim(string &str) {
string::iterator p = find_if(str.begin(), str.end(), std::not1(ptr_fun<int, int>(isspace)));
str.erase(str.begin(), p);
return str;
}
inline string& rtrim(string &str) {
string::reverse_iterator p = find_if(str.rbegin(), str.rend(), std::not1(ptr_fun<int , int>(isspace)));
str.erase(p.base(), str.end());
return str;
}
inline string& trim(string &str) {
ltrim(rtrim(str));
return str;
}
int main(){
#if _DEBUG
setlocale(LC_ALL, "chs");
#endif
string str = "\t\r\n 123sggdery中 国455 68 \r\n";
string str1 = str;
string str2 = str;
cout << "str: ~" << str << "~" << endl << endl;
cout << "ltrim(str): ~" << ltrim(str1) << "~" << endl;
cout << "rtrim(ltrim(str)): ~" << rtrim(str1) << "~" << endl << endl;
cout << "rtrim(str): ~" << rtrim(str2) << "~" << endl;
cout << "ltrim(rtrim(str)): ~" << ltrim(str2) << "~" << endl << endl;
cout << "trim(str): ~" << trim(str) << "~" << endl;
return 0;
}
上述 代码(没有设置 setlocale 的时候 )在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函数中发生了改变。最后,我把代码进行修改 添加了 setlocale 解决此问题
相关文章推荐
- 【转载】isspace函数的debug版本对中文处理有问题
- Release和Debug程序的不同及异常处理
- ArcGIS for Android 10.1.1API 中文标注导致程序异常崩溃问题
- 程序异常处理
- Oracle异常处理—ORA-12514:监听程序当前无法识别连接描述符中请求的服务
- 异常处理与程序调试
- C++ 程序DEBUG和RELEASE版本的区别
- DELPHI基础教程 第十二章 异常处理与程序调试(一)
- 为SWFUpload增加ASP版本的上传处理程序。
- CrashHandler--程序异常退出处理
- 向量化异常处理程序 继续处理程序 veh 例子
- JAVA学习第十九课(java程序的异常处理 (二))
- Ajax程序:处理异步调用中的异常(使用Asp.Net Ajax内建的异常处理方法)
- 中处理崩溃异常并重启程序
- Android:处理程序崩溃异常
- jdk1.6及之前版本IO流异常处理标准代码
- Dom4j中文异常处理:Invalid byte 2 of 2-byte UTF-8 sequence
- NASM 纯汇编打造简单中文操作系统(4 mouse.inc 鼠标驱动部分(设置鼠标中断,建立中断处理程序))
- 异常处理try...catch(sql 2005版本以上可以使用)
- Java编程程序异常处理方法