四种方法统计俄文书中元音数量的C++代码问题排查
问题描述
我尝试用四种不同方法统计列夫·托尔斯泰《战争与和平》俄文版中的元音数量,同时统计每种方法的耗时:
- 使用
count_if+find - 使用
count_if+for - 使用
for+find - 使用
for+for
但只有ForFor函数能正常输出统计结果,另外三个函数仅能输出耗时,无法显示统计结果。后续发现问题出在while(ifs>>ww):第一个调用的函数会把文件流指针读到末尾,后续函数无法再读取内容;而把循环移到main中调用各个函数时,程序完全无法运行。
问题分析与解决方案
1. 文件流指针耗尽问题
当第一个函数ForFor执行while(ifs>>ww)时,会把文件流的读取指针移动到文件末尾,后续函数调用同样的循环时,因为指针已经在末尾,循环直接不执行,自然没有统计结果。
解决方法:
每次调用统计函数前,将文件流指针重置到文件开头,并清除流的状态标志:
// 在调用每个统计函数前执行 ifs.clear(); ifs.seekg(0);
2. 计数变量作用域错误
CountIfFind和CountIfFor函数中,计数变量count定义在while循环内部,每次循环都会重新初始化,导致无法累加计数结果。需要将count移到循环外部。
修改后的CountIfFind:
void CountIfFind(std::ifstream& ifs, std::string& ww) { size_t count = 0; // 移到循环外 Timer t("count_if/find"); while (ifs >> ww) { count += std::count_if(ww.begin(), ww.end(), [&](char c) { return vowels.find(c) != std::string::npos; }); } t.print(); std::cout << count << std::endl; }
修改后的CountIfFor:
void CountIfFor(std::ifstream& ifs, std::string& ww) { size_t count = 0; Timer t("count_if/for"); while (ifs >> ww) { for (size_t i = 0; i < vowels.size(); i++) { count += std::count_if(ww.begin(), ww.end(), [&](char c) { return c == vowels[i]; }); } } t.print(); std::cout << count << std::endl; }
3. ForFind函数的逻辑错误
ForFind中使用了未赋值的char c{}来判断,应该用当前遍历的字符ww[i],否则判断的是空字符,计数永远为0:
void ForFind(std::ifstream& ifs, std::string& ww) { int count = 0; Timer t("for/find"); while (ifs >> ww) { for (size_t i = 0; i < ww.size(); i++) { // 把c改成ww[i] if (vowels.find(ww[i]) != std::string::npos) { count++; } } } t.print(); std::cout << count << std::endl; }
4. 修复containVowel函数的隐藏bug
原函数中return false写在循环内部,导致只会判断第一个字符,匹配失败就直接返回,无法遍历所有元音字符。修改后:
bool containVowel(const std::string& s, const char& a) { for (size_t i = 0; i < s.size(); i++) { if (a == s[i]) { return true; } } return false; // 移到循环外,遍历完所有字符再返回 }
完整修改后的代码示例
#include <chrono> #include <iostream> #include <vector> #include <algorithm> #include <cmath> #include <Windows.h> #include <string> #include <fstream> #include <iterator> class Timer { private: using clock_t = std::chrono::high_resolution_clock; using second_t = std::chrono::duration<double, std::ratio<1>>; std::string m_name; std::chrono::time_point<clock_t> m_beg; double elapsed() const { return std::chrono::duration_cast<second_t>(clock_t::now() - m_beg).count(); } public: Timer() : m_beg(clock_t::now()) { } Timer(std::string name) : m_name(name), m_beg(clock_t::now()) { } void start(std::string name) { m_name = name; m_beg = clock_t::now(); } void print() const { std::cout << m_name << ":\t" << elapsed() * 1000 << " ms" << '\n'; } }; const std::string vowels = "аеёиоуыэюяАЕЁИОУЫЭЮЯ"; bool containVowel(const std::string& s, const char& a) { for (size_t i = 0; i < s.size(); i++) { if (a == s[i]) { return true; } } return false; } void ForFor(std::ifstream& ifs, std::string& ww) { size_t count = 0; Timer t1("for for"); while (ifs >> ww) { for (size_t i = 0; i < ww.size(); i++) { if (containVowel(vowels, ww[i])) { count++; } } } t1.print(); std::cout << count << std::endl; } void CountIfFind(std::ifstream& ifs, std::string& ww) { size_t count = 0; Timer t("count_if/find"); while (ifs >> ww) { count += std::count_if(ww.begin(), ww.end(), [&](char c) { return vowels.find(c) != std::string::npos; }); } t.print(); std::cout << count << std::endl; } void CountIfFor(std::ifstream& ifs, std::string& ww) { size_t count = 0; Timer t("count_if/for"); while (ifs >> ww) { for (size_t i = 0; i < vowels.size(); i++) { count += std::count_if(ww.begin(), ww.end(), [&](char c) { return c == vowels[i]; }); } } t.print(); std::cout << count << std::endl; } void ForFind(std::ifstream& ifs, std::string& ww) { int count = 0; Timer t("for/find"); while (ifs >> ww) { for (size_t i = 0; i < ww.size(); i++) { if (vowels.find(ww[i]) != std::string::npos) { count++; } } } t.print(); std::cout << count << std::endl; } int main() { setlocale(LC_ALL, "ru"); SetConsoleCP(1251); SetConsoleOutputCP(1251); std::ifstream ifs; ifs.open("Толстой Лев. Война и мир. Книга 1 - royallib.ru.txt"); if (ifs.is_open()) { std::string ww; ForFor(ifs, ww); ifs.clear(); // 清除流状态 ifs.seekg(0); // 重置指针到开头 CountIfFind(ifs,ww); ifs.clear(); ifs.seekg(0); CountIfFor(ifs,ww); ifs.clear(); ifs.seekg(0); ForFind(ifs, ww); ifs.close(); } else { std::cout << "Can't open the file!" << std::endl; } }
内容的提问来源于stack exchange,提问作者Zetton
相关产品推荐
相关产品推荐

