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

如何在C++中更快读取百万行以上的CSV文件?

百万行CSV文件读取优化方案

针对百万行CSV读取的性能瓶颈,结合你的现有代码,可从以下几个方向针对性优化:

1. 预分配Vector内存

std::vector默认动态扩容会频繁触发内存重分配与元素拷贝,对百万级数据开销极大。提前预估数据量并调用reserve预留内存,彻底避免扩容开销:

std::vector<OrderBookEntry> entries;
// 按实际数据量预留(比如预估120万行,留冗余空间)
entries.reserve(1200000); 
std::ifstream csvFile{ csvFilename };

2. 加速文件IO操作

关闭标准流同步

禁用std::ifstream与C标准IO的同步机制,减少跨流调度开销:

std::ifstream csvFile{ csvFilename };
csvFile.sync_with_stdio(false);
csvFile.tie(nullptr);

一次性读取整个文件到内存

逐行getline会频繁触发磁盘IO,一次性读取整个文件到内存后再处理,能大幅减少IO交互次数:

std::ifstream csvFile{ csvFilename, std::ios::ate };
if (!csvFile.is_open()) { /* 处理文件打开失败逻辑 */ }

// 获取文件大小并分配内存
std::streampos fileSize = csvFile.tellg();
std::string fileContent(fileSize, '\0');
csvFile.seekg(0);
csvFile.read(&fileContent[0], fileSize);

// 在内存中分割行处理
size_t pos = 0;
size_t nextPos = fileContent.find('\n');
while (nextPos != std::string::npos) {
    std::string line = fileContent.substr(pos, nextPos - pos);
    // 处理当前行逻辑
    pos = nextPos + 1;
    nextPos = fileContent.find('\n', pos);
}
// 处理最后一行(若文件末尾无换行)
if (pos < fileContent.size()) {
    std::string line = fileContent.substr(pos);
    // 处理最后一行逻辑
}

3. 优化字符串分割与对象构造

用string_view替代字符串拷贝

如果你的tokenise函数会频繁创建新字符串,改用std::string_view直接引用原字符串内存,避免不必要的拷贝:

// 手动分割逗号分隔字符串,返回string_view列表(需C++17及以上)
std::vector<std::string_view> tokenise_view(const std::string& line, char delimiter) {
    std::vector<std::string_view> tokens;
    size_t start = 0;
    size_t end = line.find(delimiter);
    while (end != std::string::npos) {
        tokens.emplace_back(line.data() + start, end - start);
        start = end + 1;
        end = line.find(delimiter, start);
    }
    tokens.emplace_back(line.data() + start, line.size() - start);
    return tokens;
}

用emplace_back替代push_back

push_back会先构造临时对象再拷贝到vector,emplace_back直接在vector的内存空间中构造对象,减少一次拷贝构造:

// 原代码:entries.push_back(obe);
// 修改为直接在vector内构造对象
entries.emplace_back(price, amount, timestamp, product, orderType, username);
// 需调整stringsToOBE逻辑,直接返回构造参数或转发到emplace_back

4. 内存映射文件(进阶优化)

利用操作系统的内存映射(mmap)将文件直接映射到进程地址空间,跳过内核态到用户态的数据拷贝,IO性能最优:

#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

// 打开文件
int fd = open(csvFilename.c_str(), O_RDONLY);
if (fd == -1) { /* 处理打开错误 */ }

// 获取文件大小
struct stat sb;
if (fstat(fd, &sb) == -1) { /* 处理错误 */ }

// 映射文件到内存
char* fileData = static_cast<char*>(mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
if (fileData == MAP_FAILED) { /* 处理映射错误 */ }

// 直接操作fileData分割行与字段
// ...

// 使用完毕后释放映射与文件句柄
munmap(fileData, sb.st_size);
close(fd);

5. 减少错误处理的IO开销

当前代码每次捕获异常都会调用cout输出,频繁IO会拖慢速度。可将错误信息缓存到字符串中,最后统一输出:

std::string errorLog;
try {
    // 行处理逻辑
} catch (const std::exception& e) {
    errorLog += "CSVReader::readCSV bad data: " + std::string(e.what()) + "\n";
}
// 循环结束后统一输出错误
if (!errorLog.empty()) {
    std::cout << errorLog;
}

内容的提问来源于stack exchange,提问作者Tan Jun Rong

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 04:20:31