首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

MFC 讀取文字檔案遇到多行資料當機問題分析

     編輯:程式設計綜合問答

問題描述

在 MFC 對話方塊的 OnInitDialog() 函式中撰寫讀取文字檔案的程式碼。當資料檔案只有一行時程式正常執行,但若有兩行或以上,程式會在 CStringData* GetData() 處停止,導致當機。

原始程式碼

CStdioFile myFile;
CFileException fileException;
if (myFile.Open(_T("data.txt"), CFile::typeText | CFile::modeReadWrite), &fileException) {
    myFile.SeekToBegin();
    CString str;
    while (myFile.ReadString(str)) {
        single* fi = new single();
        int sec = 0;
        CString p;
        for (int i = 0; i < str.GetLength(); i++) {
            if (str[i] == ',') {
                if (sec == 0) // 單號
                    fi->setNum(p);
                else if (sec == 1) {
                    if (p == "0") fi->setCategory(0);
                    else fi->setCategory(1);
                }
                else if (sec == 2) fi->setSendAddr(p);
                else if (sec == 3) fi->setSendPhone(p);
                else if (sec == 4) fi->setSendNote(p);
                else if (sec == 5) fi->setReceiveAddr(p);
                else if (sec == 6) fi->setReceivePhone(p);
                else if (sec == 7) fi->setReceiveNote(p);
                else if (sec == 8) fi->setSendDate(p);
                else if (sec == 9) fi->setReceiveDate(p);
                else if (sec == 10) {
                    if (p == "0") fi->setReceived(false);
                    else fi->setReceived(1);
                }
                else if (sec == 11) {
                    if (p == "0") fi->setUnknow(0);
                    else fi->setUnknow(1);
                }
                else if (sec == 12) {
                    int num = _ttoi(p);
                    fi->setUnknowNote(num);
                }
                else {
                    double mon = _wtof(p);
                    fi->setMoney(mon);
                    sec = -1;
                }
                p = "";
                sec++;
            }
            else
                p += str[i];
        }
        tree->insert(fi);
    }
}
else {
    TRACE("Can't open file %s,error=%u\n", _T("data.txt"), fileException.m_cause);
}
myFile.Close();

問題分析與建議

程式在處理第二行資料時當機,可能原因包括:

  • 記憶體配置問題:new single() 配置的物件未正確釋放,導致記憶體洩漏或損毀。
  • 字串解析邏輯錯誤:若某行資料欄位數不足或格式不符,sec 計數會超出預期範圍,導致存取無效記憶體。
  • CString 內部資料結構損壞:當機堆疊指向 CStringData::GetData(),暗示 CString 物件內部指標異常。

除錯建議

  1. 在讀取每一行後,先印出 str 內容,確認資料格式正確。
  2. 檢查 single 類別的建構子與解構子,確保所有成員變數正確初始化。
  3. 使用智慧指標(如 std::unique_ptr)管理動態配置的 single 物件,避免記憶體洩漏。
  4. 在解析迴圈中加入邊界檢查,確保 sec 不超過預期欄位數。
  5. 觀察呼叫堆疊,確認當機發生在哪一行程式碼,進一步定位問題。

改進範例

CStdioFile myFile;
CFileException fileException;
if (myFile.Open(_T("data.txt"), CFile::modeRead | CFile::typeText, &fileException)) {
    CString str;
    while (myFile.ReadString(str)) {
        // 略過空白行
        if (str.IsEmpty()) continue;

        std::unique_ptr<single> fi = std::make_unique<single>();
        int sec = 0;
        CString field;
        bool parseError = false;

        for (int i = 0; i <= str.GetLength(); ++i) {
            // 處理最後一個欄位(無逗號結尾)
            if (i == str.GetLength() || str[i] == ',') {
                switch (sec) {
                    case 0: fi->setNum(field); break;
                    case 1: fi->setCategory(field == _T("0") ? 0 : 1); break;
                    case 2: fi->setSendAddr(field); break;
                    case 3: fi->setSendPhone(field); break;
                    case 4: fi->setSendNote(field); break;
                    case 5: fi->setReceiveAddr(field); break;
                    case 6: fi->setReceivePhone(field); break;
                    case 7: fi->setReceiveNote(field); break;
                    case 8: fi->setSendDate(field); break;
                    case 9: fi->setReceiveDate(field); break;
                    case 10: fi->setReceived(field != _T("0")); break;
                    case 11: fi->setUnknow(field == _T("0") ? 0 : 1); break;
                    case 12: fi->setUnknowNote(_ttoi(field)); break;
                    case 13: fi->setMoney(_wtof(field)); break;
                    default: parseError = true; break;
                }
                if (parseError) break;
                field.Empty();
                ++sec;
            }
            else {
                field += str[i];
            }
        }

        if (!parseError && sec == 14) { // 確認欄位數正確
            tree->insert(fi.release()); // 交由 tree 管理生命週期
        }
        else {
            TRACE(_T("資料行格式錯誤: %s\n"), str);
        }
    }
    myFile.Close();
}
else {
    TRACE(_T("無法開啟檔案: %s, 錯誤碼: %u\n"), _T("data.txt"), fileException.m_cause);
}

透過上述改進,可有效避免記憶體管理錯誤,並增強資料解析的健壯性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved