C++实现单词去重排序统计功能,不使用内置数据结构问题咨询
C++单词词频统计程序优化指导
核心需求
- 将输入文本中的单词统一转为小写,按字母顺序对去重后的唯一单词排序
- 实现词频统计,唯一单词和对应出现次数全部存储在动态数组中
- 最终将唯一单词索引列表(concordance list)和词频写入用户指定的输出文件,禁止使用vector、list等标准库现成数据结构
开发约束
- 程序需主动提示用户输入待读取的文本文件名,文件打开失败时输出错误信息;读取单词时忽略所有标点符号,所有字母转为小写消除大小写差异
- 单词排序、去重、词频统计结果全部存储在动态数组中
- 索引列表和词频写入用户指定的输出文件,写入完成后在控制台打印成功提示,同时输出完整的索引列表和词频
开发注意事项
- 采用模块化设计,拆分多个可复用函数:包括字符串小写转换、标点清理、单词搜索、数组排序、索引写入文件、从输入文件读取下一个单词等
- 所有非字母字符都视为单词分隔符
- 唯一单词总数编译期未知,需在运行时通过动态内存分配调整数组大小
- 静态数组维度必须使用符号常量定义,声明数组时直接使用该常量
- 一维数组传参时需同时传递维度,二维数组传参时传递行维度,列维度用符号常量定义
- 禁止使用标准库提供的list等现成数据结构,索引列表需实现为string类型动态数组或动态二维字符数组
现有代码问题梳理
- 逻辑冗余:main函数硬编码读取
readText.txt,后续findUnique又让用户输入文件名重复读取,且main中静态数组大小仅为100,单词数超过就会触发数组越界 - 大小写处理缺失:未实现单词转小写逻辑,大小写不同的同一个单词会被识别为不同单词
- 标点处理不完整:仅清理了单词末尾的标点,单词开头、中间的标点未处理
- 排序功能未实现:去重后的单词没有按字母顺序排序
- 输出逻辑错误:循环内重复打开输出文件,默认覆盖模式会导致最终文件仅保留最后一行数据,且输出文件名硬编码,未让用户自定义
- 内存泄漏:动态申请的数组没有手动释放
- 模块化程度不足:所有逻辑耦合在
findUnique函数中,不符合可复用要求 - 读取逻辑错误:main中混用
>>和getline读取内容会导致数据丢失,eof()判断逻辑存在缺陷,会多读空行
优化后完整代码
#include <iostream> #include <fstream> #include <string> #include <cctype> #include <iomanip> #define MAX_WORD_LEN 100 // 符号常量定义单词最大长度,符合静态数组维度要求 using namespace std; // 工具函数:将字符串全部转为小写 void toLowerString(string &str) { for (int i = 0; i < str.size(); i++) { str[i] = tolower(str[i]); } } // 工具函数:移除字符串中所有非字母字符 void removeNoneAlpha(string &str) { string res; for (int i = 0; i < str.size(); i++) { if (isalpha(str[i])) { res += str[i]; } } str = res; } // 工具函数:在单词数组中搜索目标单词,返回下标,不存在返回-1 int findWord(const string wordArr[], int arrLen, const string &target) { for (int i = 0; i < arrLen; i++) { if (wordArr[i] == target) { return i; } } return -1; } // 工具函数:按字母顺序排序单词数组和对应词频数组(冒泡排序实现) void sortConcordance(string wordArr[], int freqArr[], int arrLen) { for (int i = 0; i < arrLen - 1; i++) { for (int j = 0; j < arrLen - i - 1; j++) { if (wordArr[j] > wordArr[j + 1]) { // 交换单词 string tempWord = wordArr[j]; wordArr[j] = wordArr[j + 1]; wordArr[j + 1] = tempWord; // 交换对应词频 int tempFreq = freqArr[j]; freqArr[j] = freqArr[j + 1]; freqArr[j + 1] = tempFreq; } } } } // 工具函数:将索引列表写入指定输出文件 void writeConcordanceToFile(const string wordArr[], const int freqArr[], int arrLen, const string &fileName) { ofstream outFile(fileName); if (!outFile.is_open()) { cerr << "输出文件打开失败" << endl; return; } outFile << left << setw(20) << "单词" << "出现次数" << endl; for (int i = 0; i < arrLen; i++) { outFile << left << setw(20) << wordArr[i] << freqArr[i] << endl; } outFile.close(); cout << "索引列表已成功写入文件:" << fileName << endl; } int main() { string inputFileName; // 提示用户输入输入文件名 cout << "请输入待读取的文本文件名:" << endl; cin >> inputFileName; ifstream inFile(inputFileName); if (inFile.fail()) { cerr << "输入文件打开失败,请检查文件名是否正确" << endl; exit(-1); } // 第一遍读取统计总单词数,用于初始化动态数组 int totalWordCount = 0; string tempStr; while (inFile >> tempStr) { removeNoneAlpha(tempStr); if (tempStr.size() > 0) { // 过滤掉全是符号的空字符串 totalWordCount++; } } inFile.close(); // 重新打开文件读取单词内容 inFile.open(inputFileName); // 初始化动态数组,最大大小为总单词数(最坏情况所有单词都不重复) string *uniqueWords = new string[totalWordCount]; int *freq = new int[totalWordCount]; int uniqueCount = 0; while (inFile >> tempStr) { // 清理标点、转小写 removeNoneAlpha(tempStr); if (tempStr.size() == 0) continue; toLowerString(tempStr); // 查找单词是否已存在 int existIndex = findWord(uniqueWords, uniqueCount, tempStr); if (existIndex != -1) { freq[existIndex]++; } else { uniqueWords[uniqueCount] = tempStr; freq[uniqueCount] = 1; uniqueCount++; } } inFile.close(); // 对唯一单词排序 sortConcordance(uniqueWords, freq, uniqueCount); // 控制台输出结果 cout << endl << left << setw(20) << "单词" << "出现次数" << endl; cout << "-----------------------------------" << endl; for (int i = 0; i < uniqueCount; i++) { cout << left << setw(20) << uniqueWords[i] << freq[i] << endl; } // 提示用户输入输出文件名 string outputFileName; cout << endl << "请输入要保存的输出文件名:" << endl; cin >> outputFileName; writeConcordanceToFile(uniqueWords, freq, uniqueCount, outputFileName); // 释放动态数组内存,避免泄漏 delete[] uniqueWords; delete[] freq; return 0; }
内容的提问来源于stack exchange,提问作者Bee
相关产品推荐
相关产品推荐

