如何解决MFC中CStdioFile与CString读取文件的乱码及相关问题
问题解决方案
问题1:读取UTF-8文件到CStringW乱码
原因:直接用CStdioFile二进制模式读取UTF-8文件并存入宽字符CStringW,未做UTF-8到UTF-16的编码转换,字节与宽字符不匹配导致乱码;若文件带UTF-8 BOM(0xEFBBBF),还会引入多余干扰字符。
解决:先读取UTF-8字节到CStringA,再转成宽字符,同时处理BOM:
BOOL ReadUTF8Line(CStdioFile& file, CStringW& outLine) { CStringA lineA; if (!file.ReadString(lineA)) return FALSE; // 仅第一次读取时移除UTF-8 BOM static BOOL hasRemovedBOM = FALSE; if (!hasRemovedBOM && lineA.GetLength() >=3 && (BYTE)lineA[0]==0xEF && (BYTE)lineA[1]==0xBB && (BYTE)lineA[2]==0xBF) { lineA = lineA.Mid(3); hasRemovedBOM = TRUE; } // UTF-8转UTF-16宽字符 int len = MultiByteToWideChar(CP_UTF8, 0, lineA, -1, NULL, 0); wchar_t* buf = new wchar_t[len]; MultiByteToWideChar(CP_UTF8, 0, lineA, -1, buf, len); outLine = buf; delete[] buf; return TRUE; }
问题2:输出文件大量冗余空行
原因:代码中混合写入宽字符内容和单字节换行符"\r\n",编码不匹配导致换行解析异常;且ReadString已自动去除原文件换行符,重复添加会造成空行叠加。
解决:统一用UTF-8编码输出,将宽字符转成CStringA后写入,换行符也用单字节格式保持一致。
问题3:无法匹配关键词
原因:读取内容乱码导致关键词匹配失效,BOM字符也会干扰匹配逻辑。
解决:用上述ReadUTF8Line正确转码后,再用CStringW::Find匹配宽字符关键词即可正常识别。
修改后的完整代码
BOOL ReadUTF8Line(CStdioFile& file, CStringW& outLine) { CStringA lineA; if (!file.ReadString(lineA)) return FALSE; static BOOL hasRemovedBOM = FALSE; if (!hasRemovedBOM && lineA.GetLength() >=3 && (BYTE)lineA[0]==0xEF && (BYTE)lineA[1]==0xBB && (BYTE)lineA[2]==0xBF) { lineA = lineA.Mid(3); hasRemovedBOM = TRUE; } int len = MultiByteToWideChar(CP_UTF8, 0, lineA, -1, NULL, 0); wchar_t* buf = new wchar_t[len]; MultiByteToWideChar(CP_UTF8, 0, lineA, -1, buf, len); outLine = buf; delete[] buf; return TRUE; } void CTMCVDlg::FileIO() { CString destpath, sourcepath, destname; GetDlgItemText(IDC_EDIT1, sourcepath); // 注意:原代码重复读取IDC_EDIT1,此处需改为目标路径对应的控件ID(比如IDC_EDIT3) GetDlgItemText(IDC_EDIT3, destpath); GetDlgItemText(IDC_EDIT2, destname); destpath = GetFilePathExceptFileName(destpath); CStdioFile inF(sourcepath, CFile::modeRead | CFile::typeBinary); CStdioFile outF(destpath + destname + _T(".osu"), CFile::modeWrite | CFile::modeCreate | CFile::typeBinary); AppendText3(_T("Created and opened file \"") + destname + _T(".osu\"")); AppendText3(_T("Converting...")); CStringW szline; while (ReadUTF8Line(inF, szline)) { AppendText3(szline); CStringA outputLineA; if (szline.Find(_T("Mode:")) != -1) { CStringW modeLine = _T("Mode: 3"); int len = WideCharToMultiByte(CP_UTF8, 0, modeLine, -1, NULL, 0, NULL, NULL); char* buf = new char[len]; WideCharToMultiByte(CP_UTF8, 0, modeLine, -1, buf, len, NULL, NULL); outputLineA = buf; delete[] buf; } else { int len = WideCharToMultiByte(CP_UTF8, 0, szline, -1, NULL, 0, NULL, NULL); char* buf = new char[len]; WideCharToMultiByte(CP_UTF8, 0, szline, -1, buf, len, NULL, NULL); outputLineA = buf; delete[] buf; } outF.Write(outputLineA, outputLineA.GetLength()); outF.Write("\r\n", 2); if (szline.Find(_T("TimingPoints")) != -1) { break; } } }
额外注意点
- 原代码存在逻辑错误:两次读取
IDC_EDIT1获取路径,会导致目标路径与源路径重复,需改为对应目标路径的控件ID。 - 若原文件换行符为
\n,可将写入的换行符改为"\n",避免多余空行。
内容的提问来源于stack exchange,提问作者Edward Tsui
相关产品推荐
相关产品推荐

