Проблема с векторами структур: разыменование итераторов (вероятно, легко) - PullRequest
2 голосов
/ 03 декабря 2011

У меня проблемы с программой.Программа предназначена для получения входных данных из файла в виде имен, разделенных запятой, и подсчета их в выходной файл (до которого я еще не дошел).Это терпит неудачу прежде, чем "хорошо пока 3".Судя по всему, он ломает итератор.То есть я пытаюсь использовать его как указатель, а ++ этот указатель.Это должно перейти к следующей структуре в массиве.Затем я хочу использовать it-> inputName для доступа к чему-то внутри структуры.Однако, когда я делаю это -> inputName, он пытается использовать его в качестве указателя и не может этого сделать.Не уверен, куда идти, если я попытаюсь сделать обычный указатель типа name * it, он не будет работать с nameStorage.begin (), так как для этого требуется только итератор.Я использую Microsoft Visual Studio 2010 на Windows 7, если это поможет.

Я довольно новичок в программировании, поэтому любые другие советы также были бы полезны.Спасибо!

#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);
}

Ответы [ 5 ]

2 голосов
/ 03 декабря 2011

В этом коде есть как минимум 4 проблемы, не в последнюю очередь из-за того, что использование векторов для этого вида поиска / подсчета крайне неэффективно.

1) std :: vector :: end () возвращает специальный тип итератора, который не поддерживает булевы операторы, такие как <= (но поддерживает оператор- или operator- =). Ну, он поддерживает их, но поведение не определено. </p>

Итак

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

должно быть

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

2) Теперь, когда ваше утверждение for верно, это сравнение никогда не вернет true

else if ( it == nameStorage.end() ) {

и поэтому ваше новое значение никогда не будет сохранено в векторе. Чтобы найти tempInput в nameStorage, вы можете использовать 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<std::string, int> nameStorage;

if (nameStorage.find(tempInput) != nameStorage.end())
{
 nameStorage[tempInput]++;
} else
{
 nameStorage[tempInput] =1;
}
2 голосов
/ 03 декабря 2011

Изменение:

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:

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

Проверьте этот простой пример здесь .

1 голос
/ 03 декабря 2011

Диапазон vector равен [ begin(), end() ), end() не входит в этот диапазон допустимых элементов. end() относится к элементу (если это можно так назвать) после конца из vector.

Поэтому у вас, очевидно, возникнет проблема с разыменованием итератора, когда it == nameStorage.end(), поскольку it не будет ссылаться на существующий элемент:

if ( tempInput == it->inputName )


Соглашение должно заканчиваться на != end(), что устранит проблему «один на один» выше:

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

Идея итераторов заключается в том, чтобы внутреннее устройство было полностью скрыто от вас; значение итератора не обязательно должно быть больше значения следующего элемента.

В этом случае у вас будет на одну итерацию меньше, и условие else if (it == nameStorage.end()) никогда не будет выполнено. Однако вы можете просто добавить тело этого оператора после цикла for, поскольку после завершения цикла вы выполняете итерацию до конца vector.

0 голосов
/ 03 декабря 2011

Добавление еще одного ответа для вопросов о картах и ​​std :: getline

Карта - это ассоциативный контейнер, который связывает ключ со значением. Например, std :: map - это карта с ключом типа std :: string и значением типа int. Карты поддерживают быстрый поиск ключа (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;
 }

Наслаждайтесь.

0 голосов
/ 03 декабря 2011

Вы не добавили никаких элементов в nameStorage, поэтому вы не можете перебирать элементы этого контейнера vector. Так как вы не проверяете границы векторных итераторов правильно в цикле for, он позволяет вам войти в свой цикл, и там вы разыменовываете итератор, который ни на что не указывает. Пытаясь получить доступ к памяти, которая вам не была выделена, вы вызываете неопределенное поведение, которое, скорее всего, приведет к ошибке сегментации или какой-то другой странной проблеме, если вы повредили память, но не смогли ее вызвать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...