C++读取2GB大型txt文件存入string时getline循环耗时过长问题
性能问题核心原因
你的代码跑几小时出不了结果,根本不是getline()本身慢,是几个O(n²)复杂度的内存操作把性能完全拖垮了:
- 读文件阶段的
str = str + " " + sbook是最大瓶颈:std::string的+拼接每次执行都会重新申请新的内存块,把原有str的全部内容拷贝到新内存后再追加当前行内容。随着str长度涨到GB级,每读一行就要拷贝上GB的数据,读写开销指数级上涨,这就是你30分钟才读50万行的直接原因。 - 两个字符串处理函数效率极低:
removal()和removedoublewhitespace()每次调用erase()删除单个字符,都会把该位置后面的所有字符往前挪动1位,2GB长度的字符串按这种方式处理,总共要执行万亿次级别的内存拷贝,就算顺利读完文件,后续处理也要卡几十小时。 - 其他逻辑缺陷:
- 用
while(!book.eof())写读循环会触发经典的最后一行空读问题 - 给每个单词固定分配30字节内存,遇到长度超过30的单词会直接写越界导致程序崩溃
- 构造
leksis时读到空格就给下一个单词首位写空格,属于无意义操作,还会污染单词内容
- 用
优化实现思路
全程只做O(n)线性复杂度的操作,避免反复申请内存、反复拷贝字符串:
- 先获取文件总大小,一次性申请对应大小的内存块,把整个文件直接读入内存,跳过逐行拼接的步骤
- 给ifstream设置更大的读缓冲区(比如1MB),减少磁盘IO次数
- 用双指针法原地处理读入的内存块,一次遍历同时完成「转小写、删标点、合并连续空格」三个操作,全程不调用
erase做字符删除 - 遍历处理完的文本统计总单词数,再构造
char**数组,每个单词按实际长度分配内存,避免固定长度浪费和越界 - 二次遍历文本把每个单词拷贝到对应位置,完成构造
参考修正代码
#include <iostream> #include <fstream> #include <cctype> #include <cstring> using namespace std; // 原地处理文本:转小写、删标点、合并连续空格,返回最终有效字符长度 size_t process_text(char* buf, size_t len) { size_t write_pos = 0; bool last_is_space = true; // 开头的空格直接跳过 for (size_t read_pos = 0; read_pos < len; read_pos++) { char c = buf[read_pos]; if (isupper((unsigned char)c)) { c = tolower((unsigned char)c); } if (ispunct((unsigned char)c)) { continue; // 标点直接跳过不写入 } if (isspace((unsigned char)c)) { if (!last_is_space) { // 只保留第一个空格,后续连续空格跳过 buf[write_pos++] = ' '; last_is_space = true; } continue; } // 普通字符直接写入 buf[write_pos++] = c; last_is_space = false; } // 去掉末尾多余的空格 if (write_pos > 0 && buf[write_pos-1] == ' ') { write_pos--; } buf[write_pos] = '\0'; return write_pos; } int main() { // 打开文件,设置1MB读缓冲区加速IO const size_t BUF_SIZE = 1 << 20; char* io_buf = new char[BUF_SIZE]; ifstream book("gutenberg.txt", ios::binary); book.rdbuf()->pubsetbuf(io_buf, BUF_SIZE); // 获取文件大小 book.seekg(0, ios::end); size_t file_size = book.tellg(); book.seekg(0, ios::beg); cout << "Reading the file ....." << endl; // 一次性申请内存读入整个文件 char* file_buf = new char[file_size + 1]; book.read(file_buf, file_size); file_buf[file_size] = '\0'; book.close(); cout << "Done reading the file." << endl; cout << "Processing text (lowercase, remove punctuation, merge spaces)....." << endl; size_t valid_len = process_text(file_buf, file_size); // 统计单词总数 size_t word_cnt = 0; for (size_t i = 0; i < valid_len; i++) { if (file_buf[i] == ' ') word_cnt++; } word_cnt++; // 空格数+1就是单词数 cout << "Total words: " << word_cnt << endl; cout << "Constructing leksis....." << endl; char** leksis = new char*[word_cnt]; size_t word_idx = 0; size_t word_start = 0; for (size_t i = 0; i <= valid_len; i++) { if (i == valid_len || file_buf[i] == ' ') { size_t word_len = i - word_start; leksis[word_idx] = new char[word_len + 1]; memcpy(leksis[word_idx], file_buf + word_start, word_len); leksis[word_idx][word_len] = '\0'; word_idx++; word_start = i + 1; } } cout << "Done constructing leksis." << endl; // 业务逻辑执行位置 // 释放内存避免泄漏 for (size_t i = 0; i < word_cnt; i++) { delete[] leksis[i]; } delete[] leksis; delete[] file_buf; delete[] io_buf; return 0; }
上述实现处理2GB纯文本文件,在普通机械硬盘上耗时一般不超过30秒,SSD上速度会更快。
内容的提问来源于stack exchange,提问作者Yiannis Caravellas
相关产品推荐
相关产品推荐

