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

四种方法统计俄文书中元音数量的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 17:01:05