您的位置:首页 > 其它

关于wcout,wofstream的一些东西

2009-05-27 22:50 330 查看

关于wcout,wofstream的一些东西

遇到一个问题,解决了,做个总结:WIN32编程原来没想到把c++和WIN32放在一起做开发,上次被个缓冲区的溢出搞怕了,所以用c++的流来代替!

结果故事就开始了,文章有点长,要读做好心理准备哈![其实,要知道答案不用看前面,直接看case就晓得怎么编码了,不过,如果你懂了运行时的机制,那么你也就懂料,为什么会有这个问题,所以。。。]

总结下在VC[2005]下的CRT调用

[存在模式转换wcout,wiofstream,如果不需要的话,直接调用_fwrite(crt的ASCI运行时)写入到设备即可]:
operator<< -> streambuf::virtual streamsize __CLR_OR_THIS_CALL xsputn(const _Elem *_Ptr,streamsize _Count)
-> int __cdecl fputc (int ch,FILE *str) [msvcr80d.dll] -> int __cdecl _write_nolock (int fh, const void *buf, unsigned cnt) [write.c]
最关键的步骤在_write_nolock[这个名字说明函数已经成功通过"关键段"的同步验证]的设计步骤: [这个函数内部会判断所谓的字符模式,注意这个模式是流状态(流缓冲区)的模式]

tmode = _textmode(fh);//测试当前字符状态:__IOINFO_TM_ANSI(ASCN C模式),__IOINFO_TM_UTF8, __IOINFO_TM_UTF16LE

当前的字符模式匹配,以及console缓冲区的匹配] if (_isatty(fh) && (_osfile(fh) & FTEXT))//OK,读取当前模式
{

DWORD dwMode;
_ptiddata ptd = _getptd();
isCLocale = (ptd->ptlocinfo->lc_handle[LC_CTYPE] == _CLOCALEHANDLE);
toConsole = GetConsoleMode((HANDLE)_osfhnd(fh), &dwMode);
}

转化操作: toConsole && !(isCLocale && (tmode == __IOINFO_TM_ANSI))) //针对Console的,若当前locale和标准C不匹配,或者当前字符模式不是默认的ANSI模式,则进行转化:首先进行double convert,然后用writeFile写到设备;

简单的说,就是当前的wcout,cout会根据两点来进行核心操作:

当前字符类型,就是上面提到的字符状态:char->wchar_t,wchar_t->char,下面是调用平台相关API[windows下的WideXXXtoChar,或者相反的函数]

当前的现场[c++程序设计语言里locale类]

但这里有个陷阱,就是看上面的转化条件[若当前locale是标准c时,会进行转化么?不会的!]
说了这么多,MS的CRT宽字符转化是什么时候进行,事实是:当你的字符类型是宽字符,且locale不是标准c时才进行。[不相信的话,去VS里面F11自己看!]
好了,这就很容易解释下面的一些现象了:
case 1:
我们在默认的locale条件下,完全没有转化,直接打出,当前这是由于运行时的环境是简体中文的,因此是能输出中文的!
cout<<locale().name().c_str()<<endl;
cout<<cout.getloc().name().c_str()<<endl;
cout<<"cout 不支持输出中文? "<<endl;
case 2:
我去控制面板将系统语言更改为EN_US,locale("")会获取系统平台相关的默认语言,这个时候自然就不能输出中文了!
[顺便说一句,JRE的设置也是从系统默认语言环境中读取的,所以如果你要给老大做演示,而所有的默认系统对话框都是中文,你知道怎么做萨!]
locale::global(locale(""));//cout.imbue(std::locale("Chinese"));这句是强制的流locale绑定,呵呵,建议用这个,locale("")与系统相关,出了问题不好找
cout<<locale().name().c_str()<<endl;
cout<<cout.getloc().name().c_str()<<endl;
cout<<"cout 不支持输出中文? "<<endl;
以上的两种情况都不存在转化[double convert]问题,下面来看两个比较容易错的情况:[呵呵,开始吓了我一跳,仔细分析了下,恍然大悟]
case 3: wcout的试验
wchar_t s0[15]=TEXT("丁磊");
char s2[30];
::WideCharToMultiByte(CP_ACP, NULL, s0, _countof(s0), s2, _countof(s2), NULL, NULL);//to ASCI
cout<<s2<<endl;
wcout<<s0<<endl;
第一cout肯定是没问题的,情况和case1,case2一样,下面的wcout呢?不要惊讶,如果你追踪堆栈的话,你会发现在int __cdecl fputc(int ch,FILE *str)之后,会得到一个错误码,然后直接退出:
看看这个函数
wint_t __cdecl fputwc (
wchar_t ch,
FILE *str
)
{
REG1 FILE *stream;
REG2 wint_t retval;
_VALIDATE_RETURN((str != NULL), EINVAL, WEOF);
/* Init stream pointer */
stream = str;
_lock_str(stream);
__try {
retval = _fputwc_nolock(ch,stream);
}
__finally {
_unlock_str(stream);
}
return(retval);
}
/***
*_fputwc_nolock() - putwc() core routine (locked version)
*
*Purpose:
* Core putwc() routine; assumes stream is already locked.
*
* [See putwc() above for more info.]
*
*Entry: [See putwc()]
*
*Exit: [See putwc()]
*
*Exceptions:
*
*******************************************************************************/
wint_t __cdecl _fputwc_nolock (
wchar_t ch,
FILE *str
)
{

if (!(str->_flag & _IOSTRG))
{
if (_textmode_safe(_fileno(str)) == __IOINFO_TM_UTF16LE)
{
/* binary (Unicode) mode */
if ( (str->_cnt -= sizeof(wchar_t)) >= 0 ) {
return (wint_t) (0xffff & (*((wchar_t *)(str->_ptr))++ = (wchar_t)ch));
} else {
return (wint_t) _flswbuf(ch, str);
}
}
else if (_textmode_safe(_fileno(str)) == __IOINFO_TM_UTF8)
{
/*
* This is for files open for unicode writes. We need 2 chars
* instead of 1. Note that even if we are writing UTF8, we don't
* really need to worry about it here. _write will take care of
* proper conversion.
*/
char * p = (char *)&ch;
if(_putc_nolock(*p, str) == EOF)
return WEOF;
++p;
if(_putc_nolock(*p, str) == EOF)
return WEOF;

return (wint_t)(0xffff & ch);
}
else if ((_osfile_safe(_fileno(str)) & FTEXT))//对于标准输入在这里,前面两种是文本模式
{
int size, i;
char mbc[MB_LEN_MAX];
/* text (multi-byte) mode */
if (wctomb_s(&size, mbc, MB_LEN_MAX, ch) != 0)
{
/*
* Conversion failed; errno is set by wctomb_s;
* we return WEOF to indicate failure.
*/
return WEOF;
}
for ( i = 0; i < size; i++)
{
if (_putc_nolock(mbc[i], str) == EOF)
return WEOF;
}
return (wint_t)(0xffff & ch);
}
}
/* binary (Unicode) mode */
if ( (str->_cnt -= sizeof(wchar_t)) >= 0 )
return (wint_t) (0xffff & (*((wchar_t *)(str->_ptr))++ = (wchar_t)ch));
else
return (wint_t) _flswbuf(ch, str);
}
为什么呢?你可能有点抓狂,可是确实如此,自己犯的错,MS CRT当然不会替你买单!
问题在wchar_t s0[15]=TEXT("丁磊");//这个编码是多长,4*sizeof(wchar_t),两个中文字符在转化入wchart_t时,是以0xXX的形式放入的,虽然能看到自己的名字很爽,但是字符数组里面的存储可能让你很不爽。每个汉字的高低字节被分别存入两个wchar_t里面!俄!~~
随后的事情就很容易理解了,在下面的函数里面,CRT为你的每个wchar_t进行分别的高低字节验证,发现不符合安全验证规范[说实话,这里追进去看代码,没看的很懂的]!新的_s函数当然不买你的帐!直接返回异常码了!
/* text (multi-byte) mode */
if (wctomb_s(&size, mbc, MB_LEN_MAX, ch) != 0)
{
/*
* Conversion failed; errno is set by wctomb_s;
* we return WEOF to indicate failure.
*/
return WEOF;
}

因此对于case 3: wcout的正确写法:
wchar_t s0[15]=TEXT("丁磊");
char s2[30];
::WideCharToMultiByte(CP_ACP, NULL, s0, _countof(s0), s2, _countof(s2), NULL, NULL);//to ASCI
cout<<s2<<endl;
wcout<<s2<<endl;//有点惊讶吧,不过这是对的!
但这样还是不直观,如果更直观的搞定呢,其实很简单,我们强制的给流一个locale状态让它不是ASCI的标准状态即可![这样它就不会到FTEXT模式,而是转到相应的Unicode编码处理处了!]
case 4: wcout,cout
wchar_t s0[15]=TEXT("丁磊");
char s2[30];
::WideCharToMultiByte(CP_ACP, NULL, s0, _countof(s0), s2, _countof(s2), NULL, NULL);//to ASCI
wcout.imbue(locale("Chinese"));
cout<<s2<<endl;
wcout<<s0<<endl;
case 5: 文件操作类似
wchar_t s0[15]=TEXT("丁磊");
wofstream out("proc.txt");
if(!out) return EXIT_FAILURE;
try{
out.imbue(locale("Chinese"));//如果注释掉这行,那wofstream又会傻拉巴即的去当你的字符是FTEXT处理,自然就是被_s给砍掉了,所以proc.txt就是空
out<<L"输出0:"<<s0<<endl;//注意L,使得“输出0“也是同上处理,所以自然为空
out<<L"输出1:"<<s0<<endl;
}catch(const runtime_error& ex){
cerr<<ex.what()<<endl;//这个不是用异常处理的,所以ex肯定是永远抓不到,是用返回异常码的方式处理
}
out.close(); cout<<endl;

GCC呢?赫赫,好的多,为什么好的多呢?GCC是一个纯的编程平台,MS CRT里面还有语言包的转化处理,如果你在GCC里面这样写wchar_t *s0=L"丁磊";,GCC编译都通不过,因此你要辛苦点去处理ASCI码了,去翻汉字国标吧!顺便提一下Code::Block里面其实有wofstream的支持的,只是有点麻烦,不如直接调windows的API方便,见http://www.hardforum.com/archive/index.php/t-973922.html[所以MS方便是方便,就是有的时候,让人费解,如果想GCC那样,什么都开放出来,让你自己去看,反而没人骂了]

至于这个MS的support,呵呵,你可以当不存在,因为与其问别人,不如自己动手去看运行时的程序!http://support.microsoft.com/default.aspx/kb/274012

那天看到有人在CSDN上发贴子,不过没人管,估计是比较冷的地方吧!如果不是想将win32和c++结合起来用一下,也不会搞出这个问题来!做过了,就不难了!

一些关于locale的资料:

BJ的《c++程序设计语言》,附录里面-中文名叫《现场》locale

c++网上的库描述[GCC这块没有MS crt做的好,locale的很多东西都不能用!感觉c++的locale理念和MS的相差很远,所以几乎什么都没有!]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: