数组初始化和查找字母频率

Array initialization and finding letter frequency

我正在尝试计算字符串数组中字母出现的频率,并将频率设置为整个字母表大小的数组。我希望我已经设计了 upper/lower 个案例无关紧要的方式。在此之后,我想将频率最高的字母设置为该字母表的 '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;
}

问题 1:

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:

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

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

简单的解决方案是 运行 循环,然后测试最大的 == 0 并打印合适的 "No letters found" 消息。

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

例如计算元素。

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

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

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

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

这看起来非常直观。

另外。从地图中获取最大的计数器值,可以简单地通过使用 maxheap 来实现。可以使用 std::priority_queue 在 C++ 中实现最大堆。您可以使用它的范围构造函数,用 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 结合使用。