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

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)线性复杂度的操作,避免反复申请内存、反复拷贝字符串:

  1. 先获取文件总大小,一次性申请对应大小的内存块,把整个文件直接读入内存,跳过逐行拼接的步骤
  2. 给ifstream设置更大的读缓冲区(比如1MB),减少磁盘IO次数
  3. 用双指针法原地处理读入的内存块,一次遍历同时完成「转小写、删标点、合并连续空格」三个操作,全程不调用erase做字符删除
  4. 遍历处理完的文本统计总单词数,再构造char**数组,每个单词按实际长度分配内存,避免固定长度浪费和越界
  5. 二次遍历文本把每个单词拷贝到对应位置,完成构造
参考修正代码
#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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 10:18:42