如何对基于ULARGE_INTEGER的Size/Date列的ListView进行排序?
Win32 ListView 文件列排序方案
针对你提到的Size和Date列排序需求,以下是高效可靠的实现方案:
一、大小列排序:避免字符串转数值的隐患
你提到用std::stoi(wstr)转换大小字符串,但要注意:std::stoi针对32位int类型,而大文件(如超过4GB)的大小会超出int范围,导致溢出错误。更稳妥的做法是直接用原始的ULONGLONG数值比较——不需要从字符串转回。
二、日期列排序:不要解析格式化后的字符串
通过SHFormatDate()生成的日期字符串是本地化的(不同区域的格式、分隔符、年月日顺序都可能不同),反向解析不仅繁琐,还容易出错。最优方案是维护原始数据的并行存储,直接用数值比较。
标准实现步骤
Win32 ListView的排序通常结合ListView_SortItemsEx和自定义比较回调,通过LVITEM的lParam关联原始数据,完全规避字符串转换问题:
1. 定义存储文件原始数据的结构体
struct FileListItemData { ULONGLONG fileSize; // 原始文件大小(从WIN32_FIND_DATA组合而来) FILETIME fileLastWriteTime; // 原始文件时间(如最后修改时间) // 可添加其他需要的字段,如文件名、路径等 };
2. 添加ListView项时关联原始数据
// 假设已获取WIN32_FIND_DATA fd,hListView是ListView句柄 int nItemIndex = ListView_GetItemCount(hListView); // 分配并填充原始数据 FileListItemData* pItemData = new FileListItemData(); // 组合文件大小 ULARGE_INTEGER sizeU; sizeU.LowPart = fd.nFileSizeLow; sizeU.HighPart = fd.nFileSizeHigh; pItemData->fileSize = sizeU.QuadPart; // 保存原始文件时间(这里选最后修改时间,可根据需求替换) pItemData->fileLastWriteTime = fd.ftLastWriteTime; // 插入ListView项 LVITEM lvi = {0}; lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iItem = nItemIndex; lvi.pszText = const_cast<wchar_t*>(fd.cFileName); // 设置文件名 lvi.lParam = reinterpret_cast<LPARAM>(pItemData); // 关联原始数据 ListView_InsertItem(hListView, &lvi); // 设置大小列文本 std::wstring sizeStr = std::towstring(pItemData->fileSize); ListView_SetItemText(hListView, nItemIndex, 1, const_cast<wchar_t*>(sizeStr.c_str())); // 设置日期列文本 wchar_t dateBuf[256] = {0}; SHFormatDate(&pItemData->fileLastWriteTime, SHFD_AUTOLAYOUT, dateBuf, ARRAYSIZE(dateBuf)); ListView_SetItemText(hListView, nItemIndex, 2, dateBuf);
3. 实现排序回调函数
int CALLBACK ListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { // lParamSort传递排序列索引和方向(低16位是列索引,高16位是0=升序,1=降序) int sortCol = LOWORD(lParamSort); bool isAscending = HIWORD(lParamSort) == 0; FileListItemData* pData1 = reinterpret_cast<FileListItemData*>(lParam1); FileListItemData* pData2 = reinterpret_cast<FileListItemData*>(lParam2); int cmpResult = 0; switch (sortCol) { case 1: // 大小列排序 if (pData1->fileSize < pData2->fileSize) cmpResult = -1; else if (pData1->fileSize > pData2->fileSize) cmpResult = 1; break; case 2: // 日期列排序 // 将FILETIME转为64位数值直接比较 ULONGLONG time1 = reinterpret_cast<ULARGE_INTEGER&>(pData1->fileLastWriteTime).QuadPart; ULONGLONG time2 = reinterpret_cast<ULARGE_INTEGER&>(pData2->fileLastWriteTime).QuadPart; if (time1 < time2) cmpResult = -1; else if (time1 > time2) cmpResult = 1; break; // 其他列排序逻辑可在此扩展 } // 降序时反转结果 if (!isAscending) cmpResult = -cmpResult; return cmpResult; }
4. 触发排序(如点击列头时)
// 假设点击的列索引为nCol,当前排序方向为bAscending(true=升序) LPARAM sortParam = MAKELPARAM(nCol, bAscending ? 0 : 1); ListView_SortItemsEx(hListView, ListViewCompareProc, sortParam);
三、关于你提到的两种方案的对比
- 维护并行数据结构:这是Win32 ListView排序的标准实践,完全避免字符串解析的误差和性能损耗,逻辑清晰且可靠,是首选方案。
- C++日期时间库解析字符串:由于
SHFormatDate()的本地化特性,解析格式化后的日期字符串极易出错(如不同区域的格式差异),且步骤繁琐,不推荐使用。
内容的提问来源于stack exchange,提问作者gene b.
相关产品推荐
相关产品推荐

