C++读取超大输入文件异常:BST插入大文件首行未读取问题
大文件首行未被读取的BST程序问题
我用C++实现了一个从输入文件构建单词二叉搜索树(BST)的程序,输入文件格式如下:
i apple
i bakery
i collapse
其中命令"i"表示将后续单词插入BST。程序处理21个单词的小文件完全正常,但处理最多包含370100个单词的大文件时,首行似乎完全没被读取,找不到问题原因。相关代码如下:
class Node { public: string word; Node *left, *right, *parent; Node() // 默认构造函数 { left = right = parent = NULL; } Node(string to_insert) { word = to_insert; left = right = parent = NULL; } }; class BST { private: Node *root; public: BST(); void insert(string); void insert(Node*, Node*); }; BST :: BST() { root = NULL; } void BST :: insert(string word) { Node *to_insert = new Node(word); if (root == NULL) root = to_insert; else insert(root,to_insert); } void BST :: insert(Node* start, Node* to_insert) { if (start == NULL) return; if (to_insert->word <= start->word) { if(start->left == NULL) { start->left = to_insert; to_insert->parent = start; return; } else // 需要递归调用 { insert(start->left, to_insert); return; } } else // 插入节点的键更大,走右分支 { if(start->right == NULL) { start->right = to_insert; to_insert->parent = start; return; } else { insert(start->right, to_insert); return; } } } int main(int argc, char** argv) { if (argc < 3) // 必须提供两个输入参数 { throw std::invalid_argument("Usage: ./treewrapper <INPUT FILE> <OUTPUT FILE>"); // 抛出错误 } ifstream input; // 输入文件流 ofstream output; // 输出文件流 input.open(argv[1]); // 打开输入文件 output.open(argv[2]); // 打开输出文件 string command; // 存储下一条命令和操作 char *com, *dummy, *valstr, *op; // 用于strtok、strtol string val = ""; // 命令中的值 BST myBST; int inserted = 0; while(getline(input,command)) { cout << "Checking command" << "\n"; if (command.length() == 0) // 命令为空 continue; com = strdup(command.c_str()); // 复制字符串为C风格字符串 op = strtok(com, " \t"); // 按空格/tab分割命令,第一个token是操作 valstr = strtok(NULL, " \t"); // 下一个token是值,字符串形式 cout << valstr << "\n"; if(valstr != NULL) // 检查是否有可转换的值 val = valstr; if(strcmp(op,"i") == 0) // 插入列表 { cout << "Insert "+ val << endl; myBST.insert(val); inserted++; } } cout << "Words inserted: " << to_string(inserted) << "\n"; input.close(); output.close(); }
问题根源
- 内存泄漏与
strtok的状态异常:strdup分配的内存未释放,大文件循环会导致内存耗尽,引发程序异常中断,容易被误认为首行未读取;另外strtok依赖静态全局状态,大文件处理时可能出现解析异常。 - 文件打开状态未校验:如果大文件路径错误或权限不足,输入流打开失败但程序仍进入循环,自然无法读取首行。
- UTF-8 BOM干扰:部分大文件是带BOM的UTF-8格式,首行开头的3个隐藏字节(
0xEF 0xBB 0xBF)会导致command解析异常,跳过首行插入逻辑。 val的初始化逻辑漏洞:如果首行解析出valstr为空,val会保留上一次的空值,导致插入无效。
修复方案
核心修复点
- 用C++的
stringstream替换C风格的strtok和strdup,避免内存泄漏和状态异常; - 添加文件打开成功校验;
- 处理UTF-8 BOM;
- 优化命令解析逻辑,确保
val仅在有效时被赋值。
修复后的main函数示例
int main(int argc, char** argv) { if (argc < 3) { cerr << "Usage: ./treewrapper <INPUT FILE> <OUTPUT FILE>" << endl; return 1; } ifstream input(argv[1]); ofstream output(argv[2]); // 校验文件是否成功打开 if (!input.is_open()) { cerr << "无法打开输入文件: " << argv[1] << endl; return 1; } if (!output.is_open()) { cerr << "无法打开输出文件: " << argv[2] << endl; return 1; } // 处理UTF-8 BOM char bom[3]; input.read(bom, 3); if (!(bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF)) { input.seekg(0); // 不是BOM格式,回到文件开头 } string command; BST myBST; int inserted = 0; while (getline(input, command)) { cout << "正在处理命令: " << command << "\n"; if (command.empty()) continue; stringstream ss(command); string op, val; ss >> op >> val; if (op == "i" && !val.empty()) { cout << "插入: " << val << endl; myBST.insert(val); inserted++; } } cout << "已插入单词数: " << inserted << "\n"; input.close(); output.close(); return 0; }
内容的提问来源于stack exchange,提问作者first last
相关产品推荐
相关产品推荐

