使用地图计算词频

Count word frequency using map

这是我第一次用 C++ 实现地图。因此,给定一个包含文本的字符数组,我想计算每个单词在文本中出现的频率。我决定实现 map 来存储单词并比较后面的单词并增加一个计数器。 以下是我到目前为止编写的代码。

  const char *kInputText = "\
  So given a character array with text, I want to count the frequency of
  each word occurring in the text.\n\
  I decided to implement map to store the\n\
  words and compare following words and increment a counter.\n";      

  typedef struct WordCounts
  {
       int wordcount;
  }WordCounts;

  typedef map<string, int> StoreMap;

  //countWord function is to count the total number of words in the text.
  void countWord( const char * text, WordCounts & outWordCounts )
  {
       outWordCounts.wordcount = 0;
       size_t i;
       if(isalpha(text[0]))
            outWordCounts.wordcount++;
       for(i=0;i<strlen(text);i++)
       {
            if((isalpha(text[i])) && (!isalpha(text[i-1])))
                 outWordCounts.wordcount++;
       }
       cout<<outWordCounts.wordcount;
  }

  //count_for_map() is to count the word frequency using map.
  void count_for_map(const char *text, StoreMap & words)
  {
       string st;
       while(text >> st)
            words[st]++;
  }

  int main()
  {
       WordCounts wordCounts;
       StoreMap w;
       countWord( kInputText, wordCounts );
       count_for_map(kInputText, w);
       for(StoreMap::iterator p = w.begin();p != w.end();++p)
       {
             std::cout<<p->first<<"occurred" <<p->second<<"times. \n";
       }
       return 0;
  }



  Error: No match for 'operator >>' in 'text >> st'
  I understand this is an operator overloading error, so I went ahead and
  wrote the following lines of code.
  //In the count_for_map()
       /*istream & operator >> (istream & input,const char *text)
       {
             int i;
             for(i=0;i<strlen(text);i++)
                 input >> text[i];
             return input;
       }*/
  Am I implementing map in the wrong way?

左侧 const char*>> 没有重载。

textconst char*,而不是 istream,因此您的重载不适用(并且重载 1: 是错误的,并且 2: 已经存在于标准中图书馆)。

你想用更合适的std::istringstream,像这样:

std::istringstream textstream(text);
while(textstream >> st)
    words[st]++;

如果您使用现代 C++ 语言,那么生活会变得更加轻松。

首先。使用 std::map 是正确的方法。

这是一种或多或少的标准方法,用于计算容器中的东西。

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

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

std::unordered_map<std::string, unsigned int> counter{};
counter[word]++;

但是如何从字符串中获取单词。字符串就像一个包含元素的容器。在 C++ 中,许多容器都有迭代器。特别是对于字符串,有一个专用的迭代器允许迭代 std::string 中的模式。它被称为 std::sregex_token_iterator 并被描述为 here.。该模式以 std::regex 形式给出,这将为您提供很大的灵活性。

而且,因为我们有这么棒的专用迭代器,我们应该使用它!

所有东西结合在一起将提供一个非常紧凑的解决方案,代码行数最少。

请看:

#include <iostream>
#include <string>
#include <regex>
#include <map>
#include <iomanip>

const std::regex re{ "\w+" };
const std::string text{ R"(So given a character array with text, I want to count the frequency of 
each word occurring in the text. 
I decided to implement map to store the
words and compare following words and increment a counter.")" };


int main() {
    std::map<std::string, unsigned int> counter{};

    for (auto word{ std::sregex_token_iterator(text.begin(),text.end(),re) }; word != std::sregex_token_iterator(); ++word)
        counter[*word]++;

    for (const auto& [word, count] : counter) 
        std::cout << std::setw(20) << word << "\toccurred\t" << count << " times\n";
}