C++ - 没有匹配的构造函数

3
我想知道是否有人能帮助我。我正在尝试使用列表或结构作为容器来计算文本文件中字母的频率,并且在寻找解决方案时遇到了一些问题。
我有一些代码,首先尝试使用struct,但无法弄清如何轻松地使用指针导航这个对象。有人有添加和遍历struct条目的示例吗?struct不是一个可扩展的容器吗?
如果可以使用std::list,我宁愿这样做,但在阅读std::list文档后,在此站点或网络上并没有找到足够有用的示例。我需要一个char变量和一个整数,char包含发现的字母,整数是我找到每个字母的次数的计数器。
有谁能帮忙吗?
谢谢。
到目前为止的代码:
#include <iostream>
#include <fstream>
#include <list>
using namespace std;

struct LetterBox {
    char letter;
    size_t frequency;
    LetterBox* Next;
    LetterBox(char ch, size_t count, LetterBox* ptr)
    {
        letter = ch;
        frequency = count;
        Next = ptr;
    }
};

int main() {

    new LetterBox(' ',0,nullptr);
//    new LetterBox(' ',0,nullptr);
    int count = 0;
    char ch;
    string line;
    string FileName;

    cout << "Enter the name of the textfile: ";
    cin >> FileName;

    ifstream file (FileName);
    if (file.is_open())
        while (file.good())
        {
            while(getline(file, line)) {
                for (int i = 0; i < sizeof(line); i++) {
                    ch = toupper(line[i]);
                    count++;
                    cout << ch;

                }
            }
            file.close();
        }
    else
    {
        cout << "Unable to open file:" << FileName << endl;
        return 1;

    }
    return 0;
}

1
你应该使用映射:http://www.cplusplus.com/reference/map/map/。一般情况下,你不应该在C++中使用结构体。 - DYZ
1
为什么不直接使用std::map<char,size_t>来关联字符和其计数器? - Artemy Vysotsky
你的“LetterBox”结构可以有一个包含类的数组或列表。或者你可以使用链表。 - MKR
谢谢大家,我会考虑使用std::map作为解决方案。 - Cory Hall
1
@DYZ struct 就是默认公有访问,而不是默认私有访问的 class。你不会说在 C++ 中永远不要使用 class,那么为什么要说永远不要使用 struct 呢? - Retired Ninja
@RetiredNinja 我说的是“一般情况”。可能会有例外。然而,默认公共访问违背了封装的概念,这对于任何良好的面向对象语言来说都是至关重要的。 - DYZ
3个回答

1
您可以尝试一下,像在其他语言中使用dict一样使用std::map,它更快、更容易、更有吸引力,并且已经解决了如果您想支持UTF编码的问题。但是,如果您已经知道它仅用于ASCII文本,那么还有另一种方法。对于ASCII,您的宇宙是0-255(实际上是0-128,但为了防止有人使用扩展ASCII而忽略这一点)。这意味着我们实际上可以用合理的空间覆盖所有结果。
std::array<std::size_t, 256> letters= {0};

你可以将内部循环替换为:


for (int i = 0; i < sizeof(line); i++) {
  ch = toupper(line[i]);
  letter[ch]++;
}

然后写出结果。
for (char ch = 'A'; ch <= 'Z'; ch++) {
  std::cout << "Letter: '" << ch << "' occured " << letter[ch] << " times\n";
}

这应该与std::map的空间使用大致相同,但定位更好,查找时间也更短。您可以只使用27个字母,但这会增加更多条件。

0

正如其他人在您的问题下的评论中建议的那样,std::map 是您需要的。使用 std::map,您可以将出现次数映射到相应的字符。

这里是一个简单的例子:

#include <iostream>
#include <string>
#include <map>

int main() {
    std::string hello { "Hello, World!" };
    std::map<char, std::size_t> letterCounts;

    for(char ch : hello)
        letterCounts[ch]++;

    for(auto& elem : letterCounts)
        std::cout << elem.first << ": " << elem.second << std::endl;
}

std::list 很适合需要使用插入和删除速度快的容器。但是在 std::list 中访问元素会比较慢。


实际上,std::vector 会比 std::list 更好。现代 CPU 非常讨厌追踪指针,并且在某些预取友好的东西上表现更好。试试看;尽管 std::list 在理论上(大 O)比 std::vector 更适合插入,但我敢打赌,你几乎每次都会看到 std::vector 的表现优于它。根据我的经验,std::list 是你可以选择的最糟糕的数据结构之一,特别是对于现代 CPU。 - Jesper Juhl

0
这是一个示例,我在其中使用了mapstructured binding来计算字母的频率。
使用了c++17的新特性。

1.带初始化器的if语句

  1. 结构绑定
#include<iostream>
#include<string>
#include<map>
#include<algorithm>

int main()
{
    std::map<char,int> counterOfChar;
    std::string My("Once upon a time there was a lion who was very funny in reading text and counting them");
    std::for_each(My.begin(), My.end(), [&counterOfChar](const char& n)
    {   //'if' initialization statements are a C++1z extension
        
        if (auto [iter,success] = counterOfChar.insert(std::make_pair(toupper(n),1) ) ;success == false)
        {
            counterOfChar[iter->first] = ++iter->second;
        }
    });
    for (const auto &[character,count]:counterOfChar)
    {
        std::cout<<character<<" "<<count<<std::endl;
    }
return 0;
}

输出

  17
A 6
C 2
D 2
E 8
F 1
G 2
H 3
I 5
L 1
M 2
N 10
O 5
P 1
R 3
S 2
T 6
U 3
V 1
W 3
X 1
Y 2
Program ended with exit code: 0

我认为我已经很接近了,但由于某种原因,我的程序计算出更多的'C',但所有其他字符似乎都计算得很好。 - Cory Hall
@Cory Hall 如果你愿意的话,可以发布你的新代码。 - Hariom Singh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接