【问题标题】:Array initialization and finding letter frequency数组初始化和查找字母频率
【发布时间】:2015-07-20 02:15:28
【问题描述】:

我正在尝试计算字符串数组中字母的频率,并将频率设置为整个字母表大小的数组。我希望我已经设计了这样的方式,所以大写/小写无关紧要。在此之后,我想将频率最高的字母设置为该字母表的“e”(因为 e 在许多语言中出现的频率最高)并找出最常见的字母和 e 之间的区别。 这在我的心理演练中似乎是有道理的,但我的编译器出于某种原因给了我断点并且根本不允许我检查它,所以我不确定出了什么问题。所以请原谅我没有发布 SSCCE。提前感谢您的帮助!

#include <iostream>
#include <fstream> 

using namespace std;

int main()
{
    int alpharay[26]; 
    for (int i = 0; i < 26; i++) 
    {
        alpharay[i] = 0;
    }
    ifstream input; 
    cout << "File name (.txt): ";
    string fileName;
    cin >> fileName;
    input.open(fileName.c_str()); 
    while (!input.eof())
    {
        string newLine;
        getline (input, newLine); 
        for (int i = 0; i < newLine.length(); i++)
        {
            if (isalpha(newLine[i]))
            {
                int index;
                if (isupper(newLine[i]))
                {
                    index = newLine[i] - 'A';
                    alpharay[index]++; 
                }
                else if (islower (newLine[i]))
                {
                    index = newLine[i] - 'a'; 
                    alpharay[index]++; 
                }

            }

        }
    }
    //To find the largest value in array
    int largest = 0;
    char popular;
    for (int i = 0; i < 26; i++)
    {
        if (alpharay[i]>=largest)
        {
            largest = alpharay[i]; 
            popular = 'a' + i; 
        }
    }
    //To find the size of the shift
    int shift = popular - 'e';
    cout << "Shift size: " << shift << endl;
    return 0;
}

【问题讨论】:

  • 你的输出是什么?或任何错误信息?
  • 我无法运行它。我的编译器说它已经编译成功,但是在输入文件名后它就永远不会停止运行,屏幕上什么也没有出现。我只看到我的内存使用量不断上升......
  • 好吧,我说得对,执行了你的代码并且运行良好,确保你输入文件名作为 name.txt 并且它应该在你的代码的 .exe 所在的同一个文件夹中
  • 你传入的文件是什么?如果您没有看到输出,它似乎会在那里无限循环。
  • 输入名称作为name.txt,它会解决你的问题

标签: c++ arrays loops for-loop ifstream


【解决方案1】:

在 C++ 中,我们不应该使用 C 样式的数组,而是使用 C++ STL 容器。并且有许多容器可用于各种用途。

例如用于计数元素。

或多或少有一种标准方法来计算容器中或一般情况下的东西。

我们可以使用std::mapstd::unordered_map 之类的关联容器。在这里,我们将一个“键”(在本例中为要计数的字母)与一个值(在本例中为特定字母的计数)相关联。

幸运的是,地图有一个非常好的索引运算符[]。这将查找给定的键,如果找到,则返回对该值的引用。如果未找到,它将使用密钥创建一个新条目并返回对新条目的引用。因此,在机器人案例中,我们将获得对用于计数的值的引用。然后我们可以简单地写:

std::unordered_map<char,int> counter{};
counter[c]++;

这看起来非常直观。

另外。使用maxheap 可以简单地从地图中获取最大的计数器值。可以使用std::priority_queue 在 C++ 中实现 maxheap。您可以使用它的范围构造函数,用来自std::unordered_map 的值填充它。所以,典型的单线。现在您可以立即获得最高价值。

这样,我们可以得到一段非常紧凑的代码。

#include <iostream>
#include <fstream>
#include <utility>
#include <unordered_map>
#include <queue>
#include <vector>
#include <iterator>
#include <string>
#include <cctype>

// Some Alias names to ease up typing work and to make code more readable
using Counter = std::unordered_map<char, int>;
struct Comp { bool operator ()(const std::pair<char, int>& p1, const std::pair<char, int>& p2) { return p1.second < p2.second; }};
using MaxHeap = std::priority_queue<std::pair<char, int>, std::vector<std::pair<char, int>>, Comp>;

int main() {

    // Get filename, open file and check, if it could be opened
    if (std::string fileName{}; std::getline(std::cin, fileName)) {
        if (std::ifstream fileStream{ fileName }; fileStream) {

            Counter counter{};

            // Read all characters from the source file and count their occurence
            for (char c{}; fileStream >> c;) {

                // Get lower case of letter
                const char letter = static_cast<char>(std::tolower(c));

                // Count occurence, if letter is an alpha value
                if (std::isalpha(letter)) counter[letter]++;
            }
            // Build a Max-Heap
            MaxHeap maxHeap(counter.begin(), counter.end());

            // Show result
            std::cout << "\nShift size: " << maxHeap.top().first-'e' << '\n';
        }
        else std::cerr << "\nError: Could not open file '" << fileName << "'\n";
    }
}

使用 C++17 编译

为了方便地按排序顺序访问所有元素,您还可以使用std::multiset 代替std::priority 队列。

如果您只想拥有 n 个最顶层的元素,您可以将 std::partial_sort_copystd::vector 结合使用。

【讨论】:

    【解决方案2】:

    问题一:

    input.open(fileName.c_str()); 
    while (!input.eof())
    

    需要检查文件是否完全打开。如果文件未打开,您将永远不会收到 EOF。

    input.open(fileName.c_str()); 
    if (input.is_open()
    {
        while (!input.eof())
        // rest of your code
    }
    else
    {
        cout << "Couldn't open file " << fileName << endl;
    }
    

    但这只是包扎问题。除了 EOF 之外,您需要注意的文件还有很多可能发生的事情。

    问题 2:

    while (!input.eof())
    {
        string newLine;
        getline (input, newLine); 
        for (int i = 0; i < newLine.length(); i++)
    

    那么如果 getline 读取 EOF 会怎样?程序将其作为有效行进行处理,然后测试 EOF。同样,一个简单的修复:

    string newLine;
    while (getline (input, newLine))
    {
        for (int i = 0; i < newLine.length(); i++)
        // rest of loop code
    }
    

    只要一行读完,就继续。如果没有行,不管为什么,循环退出。

    问题 3:

    如果没有字母字符,这个循环将返回'z':

    for (int i = 0; i < 26; i++)
    {
        if (alpharay[i]>=largest)
        {
            largest = alpharay[i]; 
            popular = 'a' + i; 
        }
    }
    

    简单的解决方案是按原样运行循环,然后测试最大 == 0 并打印合适的“未找到字母”消息。

    【讨论】:

    • 感谢详细的调试。为什么问题 3 循环返回 z?我特地把它放在了主isalpha循环里面,防止算错,请解释一下。
    • 如果文件中没有字母,则所有alpharay[i] 将是0。最大的初始化为0。每个alpharay[i]&gt;=largest 将是0&gt;=0,它总是成功。循环的最后一次迭代将设置popular = 'a' + 25。 z.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多