[经验总结]利用MFC的CFile对象输出utf-8和ansi编码的文本

本文介绍了如何使用MFC的CFile对象来输出UTF-8和ANSI编码的文本文件。通过示例代码详细展示了在Windows环境下,如何进行Unicode到UTF-8的转换,并将内容写入文件,同时提供了处理ANSI编码的写入方法。

转自第三度: http://www.disandu.com/?p=875

 

// 今天写了一段输出文本到文件的代码,一开始用CFile输出中文,总是出现乱码,又是百度(Google被墙了)一番,网上的教程基本是只知其一不知其二,大多只讲了输出文件字节序标记和计算宽字符长度这两个注意点,却没有提到字符转换这个最关键的一步。下面的代码分别输出UTF-8和ANSI(简体中文Windows上是GBK编码)两种编码的文本文件。
//
// 写UTF-8文本
// 下列文件类操作中可能抛出一个CFileException异常
    try {
        CStdioFile hfile( strFilePath, CFile::modeReadWrite);
                          
        if(hfile.m_hFile)
        {
                            DWORD dwFileLen = hfile.GetLength();
                          
                            if (0 == dwFileLen) // 文件为空时写入UTF字节序标记
                            {
                                     const unsigned char LeadBytes[]  = {0xEF, 0xBB, 0xBF};
                                     hfile.Write(LeadBytes, sizeof(LeadBytes));
                            }
 
            int nSrcLen = (int)wcslen(lpstrWord);
 
            CStringA utf8String(lpstrWord);
            int nBufLen = (nSrcLen+1) * 6;
            LPSTR buffer = utf8String.GetBufferSetLength(nBufLen);
 
            // 将UNICODE 转换成UTF8
            // 需要函数AtlUnicodeToUTF8 头文件: <atlenc.h>
            int nLen = AtlUnicodeToUTF8(lpstrWord, nSrcLen, buffer, nBufLen);             // int nLen = utf8String.GetLength();
 
            buffer[nLen] = 0;
            utf8String.ReleaseBuffer();
 
            //写文件
                            hfile.SeekToEnd();
            hfile.Write((LPCSTR)utf8String, nLen);
            hfile.Write("/r/n", 2);
 
            hfile.Close();
        }
    }
    catch(CFileException* pException)
    {
        CString strMsg;
                  
        TCHAR  szErrorMessage[512];
        if (pException->GetErrorMessage(szErrorMessage,
                            sizeof(szErrorMessage)/sizeof(*szErrorMessage), 0))
                strMsg.Format(_T("(%s:%d)/n%s"), _T(__FILE__), __LINE__, szErrorMessage);
            else
                strMsg.Format(_T("(%s:%d)"),  _T(__FILE__), __LINE__);
 
        AfxMessageBox(strMsg);
    }

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// 写ANSI文本:
    try {
        CStdioFile hfile( strHeiMaiUsrLibPath, CFile::modeReadWrite);
                          
        if(hfile.m_hFile)
        {
            // 将Unicode字符串赋给Ansi格式的CStringA,实现Unicode=>Ansi转换
            CStringA utf8String(lpstrWord);
            int nLen = utf8String.GetLength();
 
            // 写文件
            hfile.SeekToEnd();
            hfile.Write((LPCSTR)utf8String, nLen);
            hfile.Write("/r/n", 2);
 
            hfile.Close();
        }
    }
    catch(CFileException* pException)
    {
        CString strMsg;
                  
        TCHAR  szErrorMessage[512];
        if (pException->GetErrorMessage(szErrorMessage, sizeof(szErrorMessage)/sizeof(*szErrorMessage), 0))
                strMsg.Format(_T("(%s:%d)/n%s"), _T(__FILE__), __LINE__, szErrorMessage);
            else
                strMsg.Format(_T("(%s:%d)"),  _T(__FILE__), __LINE__);
 
        AfxMessageBox(strMsg);
    }

<think> 我们正在讨论在英文系统中使用CFile(或C语言的文件操作)入中文时出现乱码的问题,特别是关于UTF-8ANSI编码的相关知识及解决方法。 用户引用中提供了几种方法: 引用[1]:在MFC中使用CFileUTF-8文本,通过入BOM头将Unicode字符串转换为UTF-8编码。 引用[3]:在MFC UNICODE项目中,通过入Unicode文件头(0xFEFF)并直接入宽字符(每个字符2字节)来解决乱码。 在英文系统中,由于系统默认编码不是中文字符集(如GBK),因此我们需要特别处理编码。 我们需要解释UTF-8ANSI(在Windows下通常指的是本地代码页,如GBK)的区别,以及如何正确使用它们来避免乱码。 解决方案主要围绕: 1. 使用正确的编码UTF-8ANSI文件,并确保文件有适当的标识(如BOM)以便其他程序正确识别。 2. 在入时进行必要的编码转换。 下面我们结合用户的问题,总结关于UTF-8ANSI编码的知识,并给出解决办法。 注意:在Windows环境下,ANSI编码指的是当前系统的默认代码页,对于中文系统通常是GBK,而英文系统是Windows-1252。因此,在英文系统中,如果我们将中文字符串按照ANSI编码(此时是Windows-1252)入,由于Windows-1252无法表示中文字符,就会导致乱码。 因此,在英文系统中入中文,我们通常选择UTF-8编码,因为UTF-8可以表示所有Unicode字符,且被广泛支持。 具体方法: 方法一:使用UTF-8编码文件(带BOM) - UTF-8 BOM(0xEF, 0xBB, 0xBF)作为文件开头,以标记文件UTF-8格式。 - 将字符串转换为UTF-8编码(如果源字符串是宽字符,则需要转换)。 - 以二进制模式入,避免文本模式下的字符转换。 方法二:使用宽字符(UTF-16)文件(带BOM) - 入Unicode BOM(0xFEFF)作为文件开头。 - 直接入宽字符字符串(每个字符2或4字节,Windows通常使用UTF-16,每个字符2字节)。 - 这种方法在Windows下比较常见,但跨平台性不如UTF-8。 方法三:如果目标系统支持中文ANSI(GBK),则转换为GBK编码入。但在英文系统上,由于系统默认代码页不是GBK,即使入GBK编码,系统自带的记事本等程序也会用默认的Windows-1252打开,导致乱码。因此,这种方法需要确保文件被支持GBK编码的程序打开(比如一些支持多编码的编辑器,并且手动选择GBK)。一般不推荐在英文系统上使用。 因此,在英文系统中,推荐使用UTF-8编码。 下面我们分别用C语言MFCCFile示例: 1. C语言示例(使用标准库)UTF-8: ```c #include <stdio.h> #include <locale.h> #include <wchar.h> int main() { // 设置locale为中文环境(如果系统支持的话)或UTF-8 setlocale(LC_ALL, "zh_CN.UTF-8"); // Linux环境下,Windows下可能不支持,可以尝试".65001" // UTF-8文件 FILE* fp = fopen("output.txt", "wb"); // 二进制模式打开 if (fp) { // UTF-8 BOM fputc(0xEF, fp); fputc(0xBB, fp); fputc(0xBF, fp); // UTF-8字符串 const char* utf8_str = u8"你好世界"; // C11的u8前缀确保字符串是UTF-8编码 fputs(utf8_str, fp); fclose(fp); } return 0; } ``` 但注意:在Windows英文系统中,如果使用标准C库,可能无法正确转换宽字符到UTF-8,因为默认locale可能不支持UTF-8。因此,我们可以使用Windows API进行转换。 2. 使用Windows API转换并UTF-8: ```c #include <windows.h> #include <stdio.h> int main() { // 宽字符串(Unicode) wchar_t* wstr = L"你好世界"; // 计算转换后UTF-8字符串的长度 int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); if (len == 0) { // 错误处理 return -1; } // 分配缓冲区 char* buffer = (char*)malloc(len); if (!buffer) return -1; // 转换 WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer, len, NULL, NULL); // 文件 FILE* fp = fopen("output.txt", "wb"); if (fp) { // 入BOM(可选,但推荐) fputc(0xEF, fp); fputc(0xBB, fp); fputc(0xBF, fp); fwrite(buffer, 1, len-1, fp); // len-1因为转换后的字符串包含了null终止符,我们不需要入它 fclose(fp); } free(buffer); return 0; } ``` 3. MFC中使用CFileUTF-8(如引用[1]所示): 使用`AtlUnicodeToUTF8`函数(需要atlenc.h)将Unicode字符串(CStringW)转换为UTF-8,然后入,并在文件开头入BOM。 4. UTF-16(宽字符)的方法(如引用[3]所示): ```cpp CFile file; CString filename = _T("D:\\index.html"); if (file.Open(filename, CFile::modeCreate|CFile::modeWrite)) { // 入Unicode BOM (0xFEFF) WORD wBOM = 0xFEFF; file.Write(&wBOM, 2); // 假设content是CString(在Unicode项目中是宽字符串) CString content = _T("你好世界"); file.Write((LPCTSTR)content, content.GetLength() * sizeof(TCHAR)); // 在Unicode项目中,TCHAR是wchar_t,所以每个字符2字节 file.Close(); } ``` 总结: - 在英文系统中,推荐使用UTF-8编码,并入BOM(虽然BOM在UTF-8中不是必须的,但有助于一些程序识别)。 - 如果使用宽字符(UTF-16)入,也要入BOM(0xFEFF),这样记事本等程序才能识别。 关于ANSI编码:在英文系统中,ANSI编码无法正确存储中文字符,因此应避免使用。如果必须使用ANSI(比如目标系统要求),那么需要将字符串转换为目标ANSI代码页(如GBK),但转换后在英文系统上打开仍可能乱码,除非使用支持该代码页的程序打开。 因此,优先选择UTF-8编码。 下面回答用户的问题:在英文系统中文乱码问题中UTF-8ANSI编码的相关知识及解决办法。 ### ### UTF-8ANSI编码的核心区别及乱码解决方案 #### 1. 编码本质差异 - **ANSI编码**:Windows系统的本地化字符集编码,**随系统语言设置变化**: - 中文系统默认:GBK (CP 936) - 英文系统默认:Windows-1252 (CP 1252) - 特性:**无法跨语言环境兼容**,英文系统下入的中文GBK字节会被误读为Windows-1252导致乱码[^1] - **UTF-8编码**:Unicode的变长字节实现: - 英文/数字:1字节(兼容ASCII) - 汉字:通常3字节(如“你”的UTF-8编码为`$E4 \ BD \ A0$`) - 特性:**跨平台跨语言通用**,不受系统区域设置影响 #### 2. 乱码根本原因 当文件实际编码与打开程序的解码方式不匹配时: - 在英文系统中: - ANSI格式中文 → 实际生成GBK字节 - 用记事本打开 → 按Windows-1252解码 → 乱码 - 示例:汉字“你”的GBK编码 `$ \xC4\xE3 $` 在Windows-1252下被解码为“Äã”(两个拉丁字符)[^1] #### 3. 终极解决方案:强制UTF-8编码 ##### 方案1:标准C/C++实现(跨平台) ```c #include <stdio.h> #include <string.h> void write_utf8_file(const char* filename, const char* utf8_str) { FILE* fp = fopen(filename, "wb"); if (!fp) return; // UTF-8 BOM(可选但推荐) unsigned char bom[] = {0xEF, 0xBB, 0xBF}; fwrite(bom, 1, 3, fp); // 直接UTF-8字符串 fwrite(utf8_str, 1, strlen(utf8_str), fp); fclose(fp); } // 使用示例(需确保字符串已是UTF-8编码): int main() { const char* text = u8"你好世界"; // C11特性 write_utf8_file("output.txt", text); return 0; } ``` **关键点**: - 二进制模式(`"wb"`)禁用系统编码转换 - BOM头帮助老旧系统识别编码(如Windows记事本) - C11的`u8`前缀确保源码中的字符串为UTF-8编码 ##### 方案2:Windows API转换(无C11支持时) ```c #include <windows.h> #include <stdio.h> void write_utf8_file_win(const wchar_t* wstr) { // 宽字符串转UTF-8 int utf8_len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); char* utf8_buf = (char*)malloc(utf8_len); WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8_buf, utf8_len, NULL, NULL); // 文件带BOM FILE* fp = fopen("output_win.txt", "wb"); if (fp) { fputc(0xEF, fp); fputc(0xBB, fp); fputc(0xBF, fp); fwrite(utf8_buf, 1, utf8_len-1, fp); // 去除末尾NULL fclose(fp); } free(utf8_buf); } // 使用示例 int main() { write_utf8_file_win(L"你好世界"); return 0; } ``` ##### 方案3:MFC特有方法(引用[1]优化) ```cpp void WriteUtf8File(CStringW content) { CFile mhtFile; if (!mhtFile.Open(_T("output.txt"), CFile::modeCreate | CFile::modeWrite)) return; // UTF-8 BOM BYTE bom[] = {0xEF, 0xBB, 0xBF}; mhtFile.Write(bom, sizeof(bom)); // 转换为UTF-8 CStringA utf8String; int len = WideCharToMultiByte(CP_UTF8, 0, content, -1, NULL, 0, NULL, NULL); if (len > 0) { LPSTR buffer = utf8String.GetBuffer(len); WideCharToMultiByte(CP_UTF8, 0, content, -1, buffer, len, NULL, NULL); utf8String.ReleaseBuffer(); mhtFile.Write(utf8String, len-1); // 去除NULL终止符 } mhtFile.Close(); } ``` #### 4. ANSI编码的适用场景 仅当满足**同时满足**以下条件时可用: 1. 目标系统为中文Windows(代码页936) 2. 文件**仅限本地使用**(不跨语言环境传输) ```c // 中文系统专用ANSI入 void write_ansi_gbk(const char* gbk_str) { FILE* fp = fopen("ansi.txt", "wb"); fwrite(gbk_str, 1, strlen(gbk_str), fp); fclose(fp); } ``` > ⚠️ 警告:此代码在英文系统下生成的文件用记事本打开必然乱码 --- ### 编码识别技巧 | 编码类型 | 特征字节序列 | 文本编辑器识别提示 | |----------|---------------------|--------------------------| | UTF-8 | EF BB BF (BOM) | 中文正常显示 | | UTF-16LE | FF FE | 汉字每字符占2字节 | | GBK | 无BOM,汉字双字节 | 简体中文环境下正常 | --- ### 决策流程图 ```mermaid graph TD A[英文系统中文] --> B{输出目标} B --> C1(仅英文系统使用) --> D1[UTF-8无BOM] B --> C2(跨平台通用) --> D2[UTF-8 with BOM] B --> C3(仅Windows程序) --> D3[UTF-16LE with BOM] ``` > 最佳实践:**始终优先使用UTF-8 with BOM**,兼顾最大兼容性跨平台能力。[^1][^3] §§相关问题§§ 1. 如何将ANSI编码文本转换为UTF-8编码? 2. 在Linux环境下如何正确处理中文文件? 3. 如何检测一个文本文件的真实编码格式? 4. C++中如何实现多字节宽字符字符串的相互转换? </think> ### UTF-8ANSI 编码的核心区别及乱码解决方案 #### 1. 编码本质差异 - **ANSI 编码**:Windows 系统的本地化字符集编码,**随系统语言设置变化**: - 中文系统默认:GBK (CP 936) - 英文系统默认:Windows-1252 (CP 1252) - 特性:**无法跨语言环境兼容**,英文系统下入的中文GBK字节会被误读为Windows-1252导致乱码[^1] - **UTF-8 编码**:Unicode 的变长字节实现: - 英文/数字:1 字节(兼容 ASCII) - 汉字:通常 3 字节(如“你”的 UTF-8 编码为 $E4 \ BD \ A0$) - 特性:**跨平台跨语言通用**,不受系统区域设置影响 #### 2. 乱码根本原因 当文件实际编码与打开程序的解码方式不匹配时: - 在英文系统中: - ANSI 格式中文 → 实际生成 GBK 字节 - 用记事本打开 → 按 Windows-1252 解码 → 乱码 - 示例:汉字“你”的 GBK 编码 $\small\text{C4 E3}$ 在 Windows-1252 下被解码为“Äã”(两个拉丁字符)[^1] #### 3. 终极解决方案:强制 UTF-8 编码 ##### 方案 1:标准 C/C++ 实现(跨平台) ```c #include <stdio.h> #include <string.h> void write_utf8_file(const char* filename, const char* utf8_str) { FILE* fp = fopen(filename, "wb"); if (!fp) return; // UTF-8 BOM(可选但推荐) unsigned char bom[] = {0xEF, 0xBB, 0xBF}; fwrite(bom, 1, 3, fp); // 直接UTF-8字符串 fwrite(utf8_str, 1, strlen(utf8_str), fp); fclose(fp); } // 使用示例(需确保字符串已是UTF-8编码): int main() { const char* text = u8"你好世界"; // C11特性 write_utf8_file("output.txt", text); return 0; } ``` **关键点**: - 二进制模式(`"wb"`)禁用系统编码转换 - BOM 头帮助老旧系统识别编码(如 Windows 记事本) - C11 的 `u8` 前缀确保源码中的字符串为 UTF-8 编码 ##### 方案 2:Windows API 转换(无 C11 支持时) ```c #include <windows.h> #include <stdio.h> void write_utf8_file_win(const wchar_t* wstr) { // 宽字符串转UTF-8 int utf8_len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); char* utf8_buf = (char*)malloc(utf8_len); WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8_buf, utf8_len, NULL, NULL); // 文件带BOM FILE* fp = fopen("output_win.txt", "wb"); if (fp) { fputc(0xEF, fp); fputc(0xBB, fp); fputc(0xBF, fp); fwrite(utf8_buf, 1, utf8_len-1, fp); // 去除末尾NULL fclose(fp); } free(utf8_buf); } // 使用示例 int main() { write_utf8_file_win(L"你好世界"); return 0; } ``` ##### 方案 3:MFC 特有方法(引用[1]优化) ```cpp void WriteUtf8File(CStringW content) { CFile mhtFile; if (!mhtFile.Open(_T("output.txt"), CFile::modeCreate | CFile::modeWrite)) return; // UTF-8 BOM BYTE bom[] = {0xEF, 0xBB, 0xBF}; mhtFile.Write(bom, sizeof(bom)); // 转换为UTF-8 CStringA utf8String; int len = WideCharToMultiByte(CP_UTF8, 0, content, -1, NULL, 0, NULL, NULL); if (len > 0) { LPSTR buffer = utf8String.GetBuffer(len); WideCharToMultiByte(CP_UTF8, 0, content, -1, buffer, len, NULL, NULL); utf8String.ReleaseBuffer(); mhtFile.Write(utf8String, len-1); // 去除NULL终止符 } mhtFile.Close(); } ``` #### 4. ANSI 编码的适用场景 仅当满足**同时满足**以下条件时可用: 1. 目标系统为中文 Windows(代码页 936) 2. 文件**仅限本地使用**(不跨语言环境传输) ```c // 中文系统专用ANSI入 void write_ansi_gbk(const char* gbk_str) { FILE* fp = fopen("ansi.txt", "wb"); fwrite(gbk_str, 1, strlen(gbk_str), fp); fclose(fp); } ``` > ️ 警告:此代码在英文系统下生成的文件用记事本打开必然乱码 --- ### 编码识别技巧 | 编码类型 | 特征字节序列 | 文本编辑器识别提示 | |------------|---------------------|--------------------------| | UTF-8 | EF BB BF (BOM) | 中文正常显示 | | UTF-16LE | FF FE | 汉字每字符占 2 字节 | | GBK | 无 BOM,汉字双字节 | 简体中文环境下正常 | --- ### 决策流程图 ```mermaid graph TD A[英文系统中文] --> B{输出目标} B --> C1(仅英文系统使用) --> D1[UTF-8无BOM] B --> C2(跨平台通用) --> D2[UTF-8 with BOM] B --> C3(仅Windows程序) --> D3[UTF-16LE with BOM] ``` > 最佳实践:**始终优先使用 UTF-8 with BOM**,兼顾最大兼容性跨平台能力。[^1][^3]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值