Извлечение файла, кажется, не продвигается к следующему слову в файле - PullRequest
0 голосов
/ 15 октября 2010

Я думаю, что это случилось со мной раньше.Это A3.txt:

%INSERT
MARK 29 
DAVID 21
JOHN 44
JOHN 51
LARRY 39
MARK 21
DAVID 18
JOHN 28
MARK 35
DONALD 41
PHIL 26

Даже если я использую sourcefile >> reader в конце цикла, программа продолжает выводить "reader: MARK", то есть оператор sourcefile >> reader; не работаетон продолжает получать один и тот же ввод снова и снова, или он не получает никакого ввода).

#include <iostream>
#include <fstream>
#include <string>

using namespace std;  

struct data
{
 string name;
 int id;
 data* link;
};

data* start;
data* node;
ifstream sourcefile;

int main()
{
 data* start = new data;

 start -> link = NULL;

 string input;
 string reader;
 sourcefile.open("A3.txt");

 bool firstnode = true;

 sourcefile >> input;

 node = start;

 cout << "Recieved command: " << input << endl;

 if(input == "%INSERT")
 {
  // unlike the other ones, this function keeps reading until it hits another command
  sourcefile >> reader;

  cout << "Insert name: " << reader << endl;


  // iterates through the link list until it hits the final node
  while(node -> link != NULL)
    node = node -> link;


  while(reader[0] != '%')
  {
   if(firstnode)
    start -> link = new data;
   else
    node -> link = new data;


   sourcefile >> node -> name;
   sourcefile >> node -> id;
   node -> link = NULL;

   sourcefile >> reader;
   cout << "reader: " << reader << endl;
  }
 }
 else
  return 0;

}

Тоже ... оффтоп.Компилятор сказал, что операторы switch нельзя использовать со строками, это правда, или я что-то еще не так сделал?

Ответы [ 2 ]

2 голосов
/ 16 октября 2010

Прямо сейчас ваш код делает слишком много. Программы решают совокупность подзадач в попытке решить более крупную проблему. Это приводит к принципу единой ответственности .

Это означает, что один объект (класс, функция и т. Д.) Должен решить одну проблему только . Но сейчас этого не происходит. Например, main тривиально делает больше, чем одно: он управляет узлами для списка (тоже неправильно! Ничего не удаляется!) И получает данные от пользователя. Это слишком много.

Скорее, расколоть вещи. Вы должны создать класс list, который управляет узлами, а затем main должен использовать его . Обратите внимание на разницу: main больше не решает эту проблему, она использует то, что делает.

Итак, имея это в виду, быстро следует, что чем больше мы разбиваем вещи, тем легче будет исправить, исправить и поддерживать. Получение кода и его разбиение - это «рефакторинг». Давайте сделаем это.

Во-первых, нам нужен связанный список для использования. Обычно у нас есть std::vector (примечание: связанные списки обычно являются худшим контейнером) или std::list, но поскольку ваш учитель тупой введен в заблуждение, он заставляет вас писать свои собственные. Ваше назначение должно быть либо написать контейнер списка или использовать контейнер списка и читать ввод, а не оба. (Опять же, в реальном мире мы разделяем вещи; зачем учить людей смешивать их?)

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

// normally this should be a template so it can store anything,
// and yadda yadda (more features), but let's just make it basic
// this data class is what the linked list holds
struct data
{
    std::string name;
    int id;
};

class linked_list
{
public:
    linked_list() :
    mHead(0)
    {}

    // the magic: the destructor will always run 
    // on objects that aren't dynamically allocated,
    // so we're guaranteed our resources will be
    // released properly
    ~linked_list()
    {
        // walk through the list, free each node
        while (mHead)
        {
            node* toDelete = mHead; // store current head
            mHead = mHead->next; // move to next node

            delete toDelete; // delete old head
        }
    }

    void push_back(const data& pData)
    {
        // allocate the new node
        node* newNode = new node(pData, mHead); 

        // insert
        mHead = newNode;
    }

    data pop_back()
    {
        // remove
        node* oldNode = mHead;
        mHead = mHead->next;

        // deallocate
        data d = oldNode->data;
        delete oldNode;
        return d;

        /*
        the above is *not* safe. if copying the data throws
        an exception, we will leak the node. better would be
        to use auto_ptr like this:

        // now the node will be deleted when the function ends, always
        std::auto_ptr<node> n(oldNode);

        // copy and return, or copy and throw; either way is safe
        return n->data;

        but who knows if your <strike>dumb</strike>misguided
        would allow it. so for now, make it unsafe. i doubt
        he'd notice anyway.
        */
    }

private:
    // any class that manages memory (i.e., has a destructor) also needs to
    // properly handle copying and assignment.
    // this is known as The Rule of Three; for now we just make the class
    // noncopyable, so we don't deal with those issues.
    linked_list(const linked_list&); // private and not defined means it
    linked_list& operator=(const linked_list&); // cannot be copied or assigned

    struct node
    {
        // for convenience, give it a constructor
        node(const data& pData, node* pNext) :
        d(pData),
        next(pNext)
        {}

        data d; // data we store
        node* next; // and the next node
    };

    node* mHead; // head of list
};

Теперь у вас есть список для использования. main больше не будет беспокоиться о таких вещах:

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>

using namespace std; // should generally be avoided

// your linked_list code

int main()
{
    // don't declare variables until you need them,
    // and avoid globals. (the previous rule helps)
    ifstream sourcefile("A3.txt");

    // check that it opened
    if (!sourceFile.is_open())
    {
        cerr << "could not open file" << endl;

        // EXIT_FAILURE is inside <cstdlib>
        return EXIT_FAILURE;
    }

    string input;
    sourcefile >> input;

    cout << "Received command: " << input << endl;

    linked_list datalist;
    if (input == "%INSERT")
    {
        string reader;
        sourcefile >> reader;

        cout << "Insert name: " << reader << endl;

        while (reader[0] != '%')
        {
            data d;
            d.name = reader;
            sourcefile >> d.id;

            datalist.push_back(d);

            sourcefile >> reader;
            cout << "reader: " << reader << endl;
        }
    }
}

Обратите внимание, насколько легче читать. Вы больше не управляете списком, а просто используете его. И список работает сам, поэтому вы никогда ничего не пропускаете.

Это путь, по которому вы захотите пойти: обернуть вещи в рабочие объекты, которые правильно решают одну проблему, и использовать их вместе.

2 голосов
/ 15 октября 2010
Сбой

sourcefile >> node -> id;, и после этого ни одна из операций ввода из sourcefile не выполняется, так как failbit устанавливается в потоке sourcefile.sourcefile >> node -> id; терпит неудачу, потому что он пытается прочитать целое число, но встречает "DAVID" в потоке.Это происходит потому, что sourcefile >> reader; потребляет «MARK», sourcefile >> node -> name; потребляет «29», поэтому sourcefile >> node -> id; остается с «DAVID».Попробуйте заменить sourcefile >> node -> name; на node -> name = reader.

И да, вы не можете использовать строки в switch, только целочисленные и перечислительные выражения.

В другой оффтоповой заметке вы неПохоже, что освобождает память, выделенную для узлов (простое решение: просто используйте std::list).

РЕДАКТИРОВАТЬ: Вот как может выглядеть ваша программа, если вы используете std::list:

#include <iostream>
#include <fstream>
#include <string>
#include <list>

using namespace std;  

struct data
{
 string name;
 int id;
};

ifstream sourcefile;

int main()
{
 list< data > datalist;

 string input;
 string reader;
 sourcefile.open("A3.txt");

 sourcefile >> input;

 cout << "Received command: " << input << endl;

 if(input == "%INSERT")
 {
  // unlike the other ones, this function keeps reading until it hits another command
  sourcefile >> reader;

  cout << "Insert name: " << reader << endl;

  while(reader[0] != '%')
  {
   data d;
   d.name = reader;
   sourcefile >> d.id;
   datalist.push_back( d );

   sourcefile >> reader;
   cout << "reader: " << reader << endl;
  }
 }
}
...