CStdioFile在UNICODE字符集下读写中文

来源:互联网 发布:sql 外键必须是主键吗 编辑:程序博客网 时间:2024/05/18 00:31

问题

  • 以CFile::typeBinary的形式读写包含中文的文件,未出现乱码。
  • 以CFile::typeText方式读写, 分两种情况:在多字节字符集下,使用CStdioFile::ReadString读取包含中文的文件,正常;工程编码切换至UNICODE字符集,则出现了中文乱码。

探究

查找MSDN文档,获得以下信息:

Unicode™ Stream I/O in Text and Binary Modes

When a Unicode stream I/O routine (such as fwprintf, fwscanf, fgetwc, fputwc, fgetws, or
fputws) operates on a file that is open in text mode (the default),
two kinds of character conversions take place:

  • Unicode-to-MBCS or MBCS-to-Unicode conversion. When a Unicode stream-I/O function operates in text mode, the source or destination
    stream is assumed to be a sequence of multibyte characters.
    Therefore, the Unicode stream-input functions convert multibyte
    characters to wide characters (as if by a call to the mbtowc
    function). For the same reason, the Unicode stream-output functions
    convert wide characters to multibyte characters (as if by a call to
    the wctomb function).

  • Carriage return – linefeed (CR-LF) translation. This translation occurs before the MBCS – Unicode conversion (for Unicode stream input
    functions) and after the Unicode – MBCS conversion (for Unicode
    stream output functions). During input, each carriage return –
    linefeed combination is translated into a single linefeed character.
    During output, each linefeed character is translated into a carriage
    return – linefeed combination.

However, when a Unicode stream-I/O function operates in binary mode,
the file is assumed to be Unicode, and no CR-LF translation or
character conversion occurs during input or output.

从以上信息中提取到关键信息:

  • 在文本模式下,使用UNICODE版本的IO操作函数(如_wfopen,
    fgetws等)时,函数会假定操作的对象是多字节序列(即文件存放的是多字节内容)。这些函数内部会做转换,比如读取时,就会将多字节序列转换成宽字节;写入时,就会将宽字节转换成多字节序列。

  • 在二进制模式下,函数会假定操作的对象是UNICODE序列(即文件存放的是UNICODE内容,即每个字符都用二或四(极少的情况下)个字节存储在文件中,除非显式写入,否则文件中不带BOM头)

网上搜集到的信息:

使用setlocale设置区域,原因(具体参看原文):

因为在C/C++语言标准中定义了其运行时的字符集环境为”C”,也就是ASCII字符集的一个子集,那么mbstowcs在工作时会将cstr中所包含的字符串看作是ASCII编码的字符,而不认为是一个包含有chs编码的字符串,所以他会将每一个中文拆成2个ASCII编码进行转换,这样得到的结果就是会形成4个wchar_t的字符组成的串,那么如何才能够让mbstowcs正常工作呢?在调用mbstowcs进行转换之间必须明确的告诉mbstowcs目前cstr串中包含的是chs编码的字符串,通过setlocale(
LC_ALL, “chs” )函数调用来完成,需要注意的是这个函数会改变整个应用程序的字符集编码方式,必须要通过重新调用setlocale(
LC_ALL, “C”
)函数来还原,这样就可以保证mbstowcs在转换时将cstr中的串看作是中文串,并且转换成为2个wchar_t字符,而不是4个。

代码举例:

    //区域设定    char* old_locale = _strdup( setlocale(LC_CTYPE,NULL) );    setlocale( LC_CTYPE, "chs" );    //写入中文字串    CStdioFile mFile;    if( mFile.Open( _T("test_file.txt"), CFile::modeCreate |         CFile::modeReadWrite | CFile::typeText))    {        try        {            mFile.WriteString( _T("在多字节字符集下,使用CStdioFile::ReadString")                _T("读取包含中文的文件,正常。但是将工程编码切换至"                _T("UNICODE字符集,则出现了中文乱码的情况。")) );        }        catch (CException* e)        {            e->ReportError();        }        mFile.Close();    }    //读出字段    CStdioFile mFileRead;    if ( mFileRead.Open( _T("test_file.txt"), CFile::modeRead | CFile::typeText ) )    {        CString strTemp;        mFileRead.ReadString( strTemp );        m_ctlDisplay.SetWindowText( strTemp );    }    setlocale( LC_CTYPE, old_locale );    free( old_locale );//还原区域设定

引申

使用CStdioFile读取UTF8、UNICODE(UTF16)、UTF-16LE 编码的文件:

需要用到另一个构造函数CStdioFile( FILE* pOpenStream ),传给其一个FILE对象指针,其中FILE对象是通过_tfopen_s来得到的,_tfopen_s这个函数支持打开UTF8、UNICODE(UTF16)、UTF-16LE 编码的文件,

这里写图片描述

网上也有个例子:例子

模仿着写了一份测试(测试确实可以这样用):

// 写入    FILE *fStream = NULL;    errno_t e = _tfopen_s(&fStream,         _T("text_file_utf8.txt"), _T("wt,ccs=UTF-8")); // or ccs=UTF-8    if (e != 0) return -1; // failed..CString sRead;    CStdioFile mFile( fStream );    try    {        mFile.WriteString(_T("aaaa中文文本测试11"));    }    catch (CException* e)    {        e->ReportError();    }    mFile.Close();    //读取    fStream = NULL;    e = _tfopen_s(&fStream,         _T("text_file_utf8.txt"), _T("rt,ccs=UTF-8")); // or ccs=UTF-8    if (e != 0) return -1; // failed..CString sRead;    CStdioFile mFileRead( fStream );    try    {        CString strTemp;        mFileRead.ReadString( strTemp );        m_ctlDisplay.SetWindowText( strTemp );    }    catch (CException* e)    {        e->ReportError();    }    mFileRead.Close();
0 0
原创粉丝点击