CStdioFile在UNICODE字符集下读写中文

问题

  • 以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
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值