You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 06:05:07