这个程序有点问题。该程序旨在以逗号分隔的名称形式从文件中获取输入,并在输出文件中对它们进行计数(我还没有进行计数)。它在"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;
}
。