【问题标题】:Need help combining two STL Maps into one(map<char,string> and map<string,int> into map<char,map<string,int>>)需要帮助将两个 STL 映射合二为一(map<char,string> 和 map<string,int> into map<char,map<string,int>>)
【发布时间】:2020-06-15 17:18:44
【问题描述】:

我在尝试将我的程序转换为只使用一个地图而不是两个单独的地图时遇到了一个问题。

该程序的目标是获取一个文本文件,将文件中每个字母的所有单词存储在地图map&lt;char,string&gt;(a-苹果八月,b-香蕉,c-柑橘椰子等)中,但也保留跟踪每个单词在带有地图的文本文件中出现的频率(苹果 2、八月 1、香蕉 2 等)。最后一件事是我需要在另一个文本文件中输出每个字母最常用的单词以及相应的出现次数。

我有一个程序可以执行此操作,但我需要对其进行更新,以便不再使用两个单独的映射,而是将其全部组合到一个映射 map&lt;char,map&lt;string,int&gt;&gt; 其中 char 是字母,&lt;string,int&gt; 对是以该字母开头的单词以及该单词在文本文件中出现的次数。 我面临的最大问题是编辑使用 find() 和 insert() 函数的行。

这是我当前的代码

#include <iostream>
#include <map>
#include <string>
#include <fstream>
using namespace std;
bool onlyLetters(string s) ///function which checks if a string contains only letters of the Latin alphabet
{   bool a= true;
    for(int i=0;i<s.length();i++){
        if(!(s[i]>='a' && s[i]<='z')){
                a=false;
                break;}
        else a=true;
    }
    return a;
}
void stringLowercase(string &s) ///function which converts all letters in a word to lowercase
{
    for(int i=0;i<s.length();i++)s[i]=tolower(s[i]);
}
void insertMostCommon(char a, map<string,int> occurrences, map<string,int> &result) ///function which outputs the pairs (most common word-number of occurences) in the result map
{
    int maximum=-1;
    string maxKey; ///stores the most common word for char a
    for(map<string,int>::iterator it=occurrences.begin();it!=occurrences.end();++it) {
        if(it->second > maximum && it->first[0] == a) {
            maxKey = it->first;
            maximum = it->second;
        }
    }
    result[maxKey] = maximum; ///adds the most common word for char a and how many times it appears in the text file in the result map
}

int main()
{   typedef map<string,int> siMap; ///siMap-stringintMap
    typedef map<char,string> csMap; ///csMap- charstringMap
    ifstream fin;
    ofstream fout;
    fin.open("file.txt", ios::in);
    fout.open("output.txt", ios::out);
    csMap dictionary; ///table which will store pairs letter-word
    siMap occurrences; ///table which will store pairs word-number of occurrences
    siMap result; ///end result table which will store pairs mostCommonWordForLetter-numberOfOccurrences
    string word; ///input word
    while(fin >> word) { ///reads word from file
        stringLowercase(word); ///converts the word to lowercase
        if(onlyLetters(word)){ ///string 'word' only gets input into a table if it only contains letters
                siMap::iterator it=occurrences.find(word); ///checks if the word is already in the table
                if(it!=occurrences.end()) it->second++; ///if it is, increments the count by one
                else {
                    pair<string,int> pr(word,1); ///if it's not found, creates a new pair with value 1
                    occurrences.insert(pr);
                }
                csMap::iterator it2=dictionary.find(word[0]); ///checks if the letter is already in the table
                if(it2!=dictionary.end()) it2->second+=word+" "; ///if it is, adds the word to the list of words starting with this letter
                else {
                    pair<char,string> pr(word[0],word+" "); ///if it's not found, creates a new pair letter-word
                    dictionary.insert(pr);
                }
            }
        }

    for(csMap::iterator it2 = dictionary.begin(); it2!=dictionary.end(); ++it2) {
        cout << it2->first << " " << it2->second << endl; ///prints out the pairs letter-words that start with this letter
    }
    cout << endl;
    for(siMap::iterator it = occurrences.begin(); it!=occurrences.end(); ++it) {
        cout << it->first<< " " << it->second << endl; ///prints out the number of occurrences for each word
    }
    for(char a='a'; a<='z';a++)insertMostCommon(a,occurrences,result); ///inputs the results into the end table for each letter of the alphabet
    auto it=result.begin();
    result.erase(it); ///removes the empty pair ('' -1) at the beginning of the table
    cout << endl;
    for(auto &a: result) {
       cout << a.first << " " << a.second << endl;
       fout << a.first << " " << a.second << endl; ///word-number of occurrences
    }
    fin.close();
    fout.close();
}

这是我用于测试目的的 file.txt:

citrus apple apple leg window aa asda banana banana citrus leg tree arm leg Apple 12313 aPPle coconut

这是输出.txt

apple 4
banana 2
citrus 2
leg 3
tree 1
window 1

这是控制台输出,其中还包含其他两个地图的内容:

a apple apple aa asda arm apple apple
b banana banana
c citrus citrus coconut
l leg leg leg
t tree
w window

aa 1
apple 4
arm 1
asda 1
banana 2
citrus 2
coconut 1
leg 3
tree 1
window 1

apple 4
banana 2
citrus 2
leg 3
tree 1
window 1

因此,可以看出,该程序会按照需要执行所有操作,但是我无法找到一种有效的方法来将这一切转换为仅使用一个映射 map&lt;char,map&lt;string,int&gt;&gt; 的程序。我将感谢您对此问题的任何帮助或见解,谢谢!

【问题讨论】:

    标签: c++ string dictionary stdmap


    【解决方案1】:

    我发现阅读代码有点困难,因此我建议进行一些简化并在适用的情况下使用标准算法。

    以下是代码中使用 cmets 的示例:

    #include <algorithm>
    #include <cctype>
    #include <cstdint>
    #include <iostream>
    #include <map>
    #include <sstream>
    
    int main() {
        std::istringstream file(
            "citrus apple apple leg window aa asda banana banana "
            "citrus leg tree arm leg Apple 12313 aPPle coconut");  
    
        std::map<char, std::map<std::string, std::uintmax_t>> res;
        std::string word;
    
        // populate the container
        while(file >> word) {
    
            // transform to lowercase
            std::transform(word.begin(), word.end(), word.begin(),
                [](char ch) {
                    return std::tolower(ch);
                }
            );
    
            // add char and word if it only contains letters
            if(
              std::all_of(word.begin(), word.end(), [](char ch){return std::isalpha(ch);})
            ) {
                // res[word[0]]  Creates or returns a reference to an existing
                //               map<string,uintmax_t>.
                // [word]        Creates or uses an existing pair<string,uintmax_t> and
                //               returns a reference to the uintmax_t.
                // ++            Adds 1 to the uintmax_t
                ++res[word[0]][word];
            }
        }
    
        // print result in C++17 and above:
        /*
        for(const auto& [ch, scmap] : res) {
            std::cout << ch << " (words: " << scmap.size() << "):\n";
            for(const auto& [word, count] : scmap) {
                std::cout << ' ' << count << ' ' << word << '\n';
            }
        }
        */
    
        // print result in C++11 or C++14:
        for(const auto& ch_scmap : res) {
            char ch = ch_scmap.first;
            auto& scmap = ch_scmap.second;
            std::cout << ch << " (words: " << scmap.size() << "):\n";
            for(const auto& word_count : scmap) {
                const auto& word = word_count.first;
                auto& count = word_count.second;
                std::cout << ' ' << count << ' ' << word << '\n';
            }
        }
    }
    

    Demo

    【讨论】:

    • 非常感谢您为构建此程序所做的努力!但是,当我尝试运行它时,我在构建消息中弹出了相当多的错误,第一个是在第 42 行关于 ''error: expected unqualified-id before '[' token''。我尝试在编译器中切换到其他标准,但这似乎并没有解决问题,所以我假设问题出在其他地方?
    • @Pulis 它需要 C++17。您使用的是较旧的 C++ 版本吗?如果有,是哪个版本?
    • 我想我可能仍然在 C++11 或 C++14 上留下了复选标记,但是即使我将 CodeBlocks 中的编译器设置更改为“让 g++ 遵循即将到来的 C+” +1z(aka C++17) ISO C++ 语言标准 [-std=c++1z]'',我仍然遇到同样的错误。我假设这可能是我的 CodeBlocks 安装的问题?
    • @Pulis C++1z 是编译器在发布之前将其称为 C++17,因此您的编译器有点旧。我会尝试获得更新的编译器。无论如何,只有最后的打印需要 C++11 以外的任何东西,所以我会改变那部分。几分钟后...编辑:完成
    • 哇,我非常感谢您为我写了所有这些,我真的很感激!我很确定我可以设法为源代码中的 insertMostCommon 函数编写适当的替换,所以我认为应该是这样。最后一件事,uintmax_t 的使用是否特定于更现代的 C++ 代码?我认为这是我第一次在我的编程水平的代码中遇到它,所以如果这是一个愚蠢的问题,我深表歉意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    相关资源
    最近更新 更多