结构体向量的问题:解约束迭代器(可能很容易)



这个程序有点问题。该程序旨在以逗号分隔的名称形式从文件中获取输入,并在输出文件中对它们进行计数(我还没有进行计数)。它在"good so far 3"之前失败了。显然,它破坏的是保护迭代器。也就是说,我要做的是把它当作一个指针,然后++这个指针。这应该直接跳到数组中的下一个结构体。然后,我想使用它->inputName来访问结构体内部的东西。然而,当我这样做->inputName,它试图使用它作为一个指针,它不能这样做。不知道从哪里开始,如果我尝试创建一个像name * it这样的常规指针,它不能与nameStorage.begin()一起工作,因为它只需要一个迭代器。我在windows 7上使用Microsoft Visual Studio 2010,如果有帮助的话。

我对编程相当陌生,所以任何其他提示也会很棒。谢谢你!

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
struct name{
    int tally;
    string inputName;
};
bool die( const string& message );
int main( void ) {
    ifstream infile;
    infile.open( "input.txt", ifstream::in );
    vector<name> nameStorage; // stores all names and tallies of them
    vector<name>::iterator it;
    string tempInput; // Stores the most recent input temporarily
    char temp;
    int counter = 0;
    while ( temp = infile.get(), infile.good() ) { // loop while extraction from file is possible
        if ( temp != ',' ) {
            tolower ( temp ); // makes everything lowercase
            tempInput.push_back(temp); }
        else {
            cout<<"good so far"<<endl;
            for ( it = nameStorage.begin(); it <= nameStorage.end(); it++ ) {
                cout<<"good so far 2"<<endl;
                if ( tempInput == it->inputName ) { 
                    cout<<"good so far 3"<<endl;
                    it->tally++;
                    break;
                }
                else if ( it == nameStorage.end() ) {
                    name tempStruct;
                    tempStruct.inputName = tempInput;
                    tempStruct.tally = 1;
                    nameStorage.push_back( tempStruct );
                }
            }
            tempInput.clear(); // clears the string after it has stored it
        }
        counter++;
        cout<<"Detected "<<counter<<" characters so far."<<endl;
    }
}
bool die( const string& message ) {
    cerr<<message;
    exit (EXIT_FAILURE);
}

这段代码至少有4个问题,其中最重要的是使用向量进行这种查找/计数是非常低效的。

1) std::vector::end()返回一个特殊类型的迭代器,它不支持<=等布尔操作符(但支持operator-或operator-=)。嗯,它支持它们,但是行为是未定义的。

for ( it = nameStorage.begin(); it <= nameStorage.end(); it++ )
应该

for ( it = nameStorage.begin(); it != nameStorage.end(); it++ )
现在for语句是正确的,这个比较将永远不会返回true
else if ( it == nameStorage.end() ) {

,因此新值永远不会存储在vector中。要在nameStorage中查找tempInput,您可以使用std::find

if (std::find(nameStorage.begin(), nameStorage.end(), tempInput) != nameStorage.end())
{
 /// temp struct blah blah blah
 nameStorage.push_back(tempStruct);
}

3)在字符串上使用布尔等价运算符通常被认为是不好的形式,即

if ( tempInput == it->inputName ) {
应该

if (!tempInput.compare(it->InputName))

,但是如果你使用std::find(上面的),你就不需要这样做了。

4) std::getline支持分隔符,您应该使用它而不是一次读取1个字符,参见http://www.cplusplus.com/reference/string/getline/

最后,您应该真正使用映射执行查找/计数。Std:map是fine

std::map<std::string, int> nameStorage;
if (nameStorage.find(tempInput) != nameStorage.end())
{
 nameStorage[tempInput]++;
} else
{
 nameStorage[tempInput] =1;
}

变化:

for ( it = nameStorage.begin(); it <= nameStorage.end(); it++ )

:

for ( it = nameStorage.begin(); it != nameStorage.end(); it++ )

:

else if ( it == nameStorage.end() ) {
          name tempStruct;
          tempStruct.inputName = tempInput;
          tempStruct.tally = 1;
          nameStorage.push_back( tempStruct );
        }

在你的for循环中,你不需要else if,因为这是for循环结束的条件:

name tempStruct;
tempStruct.inputName = tempInput;
tempStruct.tally = 1;
nameStorage.push_back( tempStruct );

查看这个简单的例子

a vector的范围是[ begin(), end() ), end()不包括在该有效元素范围内。end()指的是在vector结尾之后的元素(如果可以这样称呼它的话)。

因此,当it == nameStorage.end()解引用迭代器时,显然会遇到问题,因为it不会引用现有元素:

if ( tempInput == it->inputName )


惯例是在!= end()上终止,这将消除上面的off-by- 1问题:

for ( it = X.begin(); it != X.end(); it++ )

迭代器的思想是将内部完全隐藏起来;迭代器的值不一定大于下一个元素的值。

在这种情况下,您将少进行一次迭代,并且条件else if (it == nameStorage.end())永远不会为真。然而,您可以简单地将语句体添加到for循环的之后,因为一旦循环结束,您就迭代到了vector的末尾。

您没有向nameStorage添加任何元素,因此您不能遍历该vector容器的元素。因为你没有在for循环中正确地检查vector迭代器的边界,它允许你进入你的循环,在那里你解引用了一个没有指向任何东西的迭代器。通过尝试访问内存,你还没有分配,你调用未定义的行为,这将最有可能导致分割错误或一些其他奇怪的问题,如果你破坏内存,但未能崩溃。

添加关于映射和std的问题的另一个答案::getline

map是将键与值关联起来的关联容器。例如,std::map是一个具有std::string类型键和int类型值的map。映射支持键的快速查找(log(n))。如:

std::map<std::string, int> nameCount;

是一个映射,它将存储与字符串键相关联的int值。(参见std::getline解释后的代码示例)。

std::getline将指定的分隔符视为行结束符。使用它的通常方法是首先获得由EOL分隔的整行,然后使用分隔符从该行获取行。', '

下面的代码可以达到你想要的效果。

 std::map<std::string, int> nameCount;
 std::ifstream myfile(filename.c_str(),std::ios::in);
 std::string line;
 while(std::getline(myfile, line))
 {
    std::stringstream linestream(line);
    std::string       name;
    while (std::getline(linestream, name, ','))
    {
      if (nameCount.find(name) != nameCount.end())
      {
        nameCount[name]++; // increases the tally for name
      }
      else
      {
        nameCount[name] = 1; // inserts name into map and sets the tally to 1
      }
    }
 }
 // now output the map
 std::map<std::string, int>::iterator namecountitr;
 for (namecountitr = nameCount.begin(); 
       namecountitr != nameCount.end(); namecountitr++)
 {
      std::cout << namecountitr->first.c_str() << ":" 
      << namecountitr->second << std::endl;
 }

最新更新