C++中CLISTCTRL读写CSV异常:索引11后重复截断问题排查
C++中CLISTCTRL加载CSV时索引11后重复覆盖问题排查与修复
问题场景
将CLISTCTRL内容保存为.csv文件后重新加载,索引0到10显示正常,但从索引11开始出现重复输出(覆盖索引10内容)且字符截断的情况。
原始代码
struct DATA { CString strName; CString strEmail; CString strTel; CString strGroup; }; //Code to initialize CLISTCTRL //CLISTCTRL control name = L_viewctrl FILE* file = NULL; DATA* indexData = new DATA; USES_CONVERSION; fopen_s(&file, path_str, "r"); if (file) { char szContent[2048]; memset(szContent, NULL, sizeof(szContent)); CString strRstring; int nFileline(0); int nSubString(0); while (fgets(szContent, 2048, file)) { //File Read CString strFile; strFile = szContent; bool bfileLineRet = TRUE; while (bfileLineRet) { AfxExtractSubString(strRstring, strFile, nSubString++, ','); if (nSubString == 2)indexData->strName = strRstring; if (nSubString == 3)indexData->strEmail = strRstring; if (nSubString == 4)indexData->strTel = strRstring; if (nSubString == 5) { indexData->strGroup = strRstring; nSubString = 0; bfileLineRet = FALSE; } } //LIST INDEX DATA SET; UpdateData(TRUE); CString strCount; strCount.Format(_T("%d"), nFileline); L_viewctrl.InsertItem(nFileline, strCount); L_viewctrl.SetItem(nFileline, 1, LVIF_TEXT, indexData->strName, 0, 0, 0, 0); L_viewctrl.SetItem(nFileline, 2, LVIF_TEXT, indexData->strEmail, 0, 0, 0, 0); L_viewctrl.SetItem(nFileline, 3, LVIF_TEXT, indexData->strTel, 0, 0, 0, 0); L_viewctrl.SetItem(nFileline, 4, LVIF_TEXT, indexData->strGroup, 0, 0, 0, 0); nFileline++; } } fclose(file);
问题排查
nSubString初始化位置错误:该变量定义在循环外部,若某一行解析失败(字段不足5个),nSubString不会被重置,导致下一行解析时索引错位,数据赋值混乱。DATA对象复用未清空:仅创建一个全局DATA对象,解析出错时旧数据残留,直接复用导致后续行显示重复内容。- 换行符未处理:
fgets会将换行符\n读入缓冲区,污染最后一个字段内容,引发显示截断或异常。 - 无效的
UpdateData(TRUE)调用:循环内每次插入行都调用该函数,强制同步控件数据到变量,干扰控件正常更新逻辑。
修正方案
struct DATA { CString strName; CString strEmail; CString strTel; CString strGroup; }; //Code to initialize CLISTCTRL //CLISTCTRL control name = L_viewctrl FILE* file = NULL; USES_CONVERSION; fopen_s(&file, path_str, "r"); if (file) { char szContent[2048]; int nFileline(0); while (fgets(szContent, 2048, file)) { // 每行解析创建局部DATA对象,自动清空旧数据 DATA indexData; // 移除换行符,避免污染字段内容 size_t len = strlen(szContent); if (len > 0 && szContent[len-1] == '\n') { szContent[len-1] = '\0'; if (len > 1 && szContent[len-2] == '\r') { szContent[len-2] = '\0'; } } CString strFile = szContent; CString strRstring; // 每次解析新行时重置子字符串索引 int nSubString = 0; // 按顺序提取字段,增加提取成功判断 if (AfxExtractSubString(strRstring, strFile, nSubString++, ',')) indexData.strName = strRstring; if (AfxExtractSubString(strRstring, strFile, nSubString++, ',')) indexData.strEmail = strRstring; if (AfxExtractSubString(strRstring, strFile, nSubString++, ',')) indexData.strTel = strRstring; if (AfxExtractSubString(strRstring, strFile, nSubString++, ',')) indexData.strGroup = strRstring; // 插入数据到列表控件 CString strCount; strCount.Format(_T("%d"), nFileline); L_viewctrl.InsertItem(nFileline, strCount); L_viewctrl.SetItem(nFileline, 1, LVIF_TEXT, indexData.strName, 0, 0, 0, 0); L_viewctrl.SetItem(nFileline, 2, LVIF_TEXT, indexData.strEmail, 0, 0, 0, 0); L_viewctrl.SetItem(nFileline, 3, LVIF_TEXT, indexData.strTel, 0, 0, 0, 0); L_viewctrl.SetItem(nFileline, 4, LVIF_TEXT, indexData.strGroup, 0, 0, 0, 0); nFileline++; } fclose(file); }
关键修复点说明
- 将
nSubString移至循环内部:每次解析新行都从0开始索引,避免字段错位。 - 使用局部
DATA对象:每行解析创建新的栈对象,自动清空旧数据,无需手动重置。 - 处理换行符:移除
fgets读取的\n和\r,保证字段内容干净。 - 移除
UpdateData(TRUE):直接操作控件即可,无需同步变量,避免干扰更新逻辑。 - 增加字段提取判断:通过
AfxExtractSubString的返回值判断提取是否成功,防止索引越界。
内容的提问来源于stack exchange,提问作者포스제이
相关产品推荐
相关产品推荐

