0

I wrote the following function to read from a file and load the "record" into a BST in visual studio 2015. It works as I intended it to in windows but when I run the function on a mac, it results in an infinite loop. I can't figure out why. Can someone shed some light on it. UPDATED with full code, the problem is with the following function:

BinarySearchTree readFile()
{
    ifstream myfile;
    myfile.open("character.txt");
    string name;
    string aString = "Empty";
    BinarySearchTree loadTree;
    List list1;
    int index = 1;
    bool finishRecord = false;
    if (!myfile.peek() == myfile.eof())
    {
        while (!myfile.eof())
        {
            getline(myfile, name);
            while (!finishRecord)
            {
                getline(myfile, aString);
                if (aString == "/")
                {
                    finishRecord = true;
                }
                else
                {
                    list1.insert(index, aString);
                    index += 1;
                }
            }

            KeyedItem an_item(name, list1, true);
            loadTree.searchTreeInsert(an_item);

            //reset variables used for each record
            index = 1;
            list1.removeAll();
            finishRecord = false;
        }
    }
    myfile.close();
    return loadTree;
}   // end readFile

full code:

#include <iostream>     //needed for input and output and the console screen
#include <fstream>      //needed to read/write a file
#include <iomanip>
#include <algorithm>    //needed to use the transform function

#include "ListP.h"  //linked list class
#include "BST.h" // binary tree operations

using namespace std;

string suspectsArray[256];  //array of the suspects
int suspectIndex = 0;
KeyedItem ADD();
BinarySearchTree readFile();
void QUIT(BinarySearchTree& passedTree, string addedItems[], int arrayIndex);
bool withinTree(BinarySearchTree& passedTree, string name);
string INQUIRY(BinarySearchTree& passedTree);
void suspects(TreeItemType& anItem);
void findTip(BinarySearchTree& passedTree, string tip, int &noSuspects);


int main()
{
    string aString;
    BinarySearchTree characterTree = readFile();
    KeyedItem an_Item;
    string addedItems[256] = {};
    int index = 0;
    string answer;
    string inquiryReturn = "";
    bool main = false;
    bool inTree = false;

    while (!main)
    {
        cout << "WE ARE AT MAIN LOOP. YOU CAN ADD, INQUIRY, OR QUIT";
        cout << endl;
        if (inquiryReturn == "")
        {
            cin >> answer;
            cin.ignore();   //need to flush the input for the getline()
        }
        else
        {
            inquiryReturn = "";
        }


        //since we need to accept the input regardless of it case
        //so going to make the input into lower case
        transform(answer.begin(), answer.end(), answer.begin(), tolower);
        //determine which operation the user called
        if (answer == "add")    //user wishes to ADD a character
        {
            cout << endl;
            an_Item = ADD();
            //need to determine if the record for the character
            //is already in the tree or not
            inTree = withinTree(characterTree, an_Item.getKey());
            if (!inTree)
            {
                addedItems[index] = an_Item.getKey();
                index += 1;
                characterTree.searchTreeInsert(an_Item);
            }
            else
            {
                cout << "Character already in tree." << endl;
            }
            inTree = false;
        }
        else if (answer == "inquiry")   //user wishes to do an INQUIRY
        {
            cout << "User wants to do an INQUIRY" << endl;

            characterTree.postorderTraverse(suspects);
            inquiryReturn = INQUIRY(characterTree);
            answer = inquiryReturn;
            suspectIndex = 0;
        }
        else if (answer == "quit")      //user wishes to QUIT the program
        {
            if (index > 0)      //if the user added a character, save
            {
                cout << "OK, saving database to file "
                    "\"character.txt\" ";
                QUIT(characterTree, addedItems, index);
                cout << "... Done.  Goodbye." << endl;
                main = true;
            }
            else
            {
                cout << "OK, saving database to file "
                    "\"character.txt\" ";
                cout << "... Done.  Goodbye." << endl;
                main = true;
            }
        }
        else
        {
            cout << "You didn't enter a valid command." << endl;
        }

    }

    return 0;
}   //end main

void suspects(TreeItemType& anItem)
{
    suspectsArray[suspectIndex] = anItem.getKey();
    suspectIndex++;
}

 //Function to get a record to add to the binary search tree
 //gets the record in the type KeyedItem
KeyedItem ADD()
{
    string name;
    string a_string;
    List list1;
    int index = 1;
    bool end = false;

    cout << "OK, we are now adding a character to the database."
        << endl;
    cout << "Name of Character:" << setw(5) << " ";
    getline(cin, name);
    cout << "Attributes:" << setw(12) << " ";

    //loop to get attributes if any
    while (!end)
    {
        getline(cin, a_string);
        if (a_string == "")
        {
            //if no attributes were added, need to prompt for one
            if (list1.getLength() == 0)
            {
                cout << "Need to enter at least one attribute." <<
                    endl << setw(23) << " ";
            }
            else
            {
                cout << endl;
                end = true;
            }
        }
        else
        {
            list1.insert(index, a_string);
            index += 1;
            cout << setw(23) << " ";
        }
    }
    KeyedItem an_item1(name, list1, true);
    return an_item1;
}   // end ADD

//Function to read the database file and 
//load all of the records into a Binary Search Tree.
BinarySearchTree readFile()
{
    ifstream myfile;
    myfile.open("character.txt");
    string name;
    string aString = "Empty";
    BinarySearchTree loadTree;
    List list1;
    int index = 1;
    bool finishRecord = false;
    if (!myfile.peek() == myfile.eof())
    {
        while (!myfile.eof())
        {
            getline(myfile, name);
            while (!finishRecord)
            {
                getline(myfile, aString);
                if (aString == "/")
                {
                    finishRecord = true;
                }
                else
                {
                    list1.insert(index, aString);
                    index += 1;
                }
            }

            KeyedItem an_item(name, list1, true);
            loadTree.searchTreeInsert(an_item);

            //reset variables used for each record
            index = 1;
            list1.removeAll();
            finishRecord = false;
        }
    }
    myfile.close();
    return loadTree;
}   // end readFile

//Function to run if additional shady characters were manually added to the Binary Search Tree
//It should the added characters to the database text file.
void QUIT(BinarySearchTree& passedTree, string addedItems[], int arrayIndex)
{
    ofstream outfile;   //variable that will be assigned to the file
    KeyedItem an_item;  //record that needs to be added to file
    string aString;
    outfile.open("character.txt", ios::app);
    if (outfile.is_open())
    {
        for (int i = 0; i < arrayIndex; i++)
        {
            passedTree.searchTreeRetrieve(addedItems[i], an_item);
            outfile << an_item.getKey() << "\n";
            int jkl = an_item.attributes.getLength();
            for (int bnm = 1; bnm < jkl + 1; bnm++)
            {
                an_item.attributes.retrieve(bnm, aString);
                outfile << aString << "\n";
            }
            outfile << "/\n";
        }
        outfile.close();
    }
}   // end QUIT


//function to check whether or not the character is already in the tree
bool withinTree(BinarySearchTree& passedTree, string name)
{
    KeyedItem item;
    try
    {
        passedTree.searchTreeRetrieve(name, item);
    }
    catch (TreeException& error)
    {
        //the character is not within the tree
        return false;
    }
    //the character is within the tree
    return true;
}   // end withinTree

//Function to if the tip matches any of the attributes of the suspects
void findTip(BinarySearchTree& passedTree, string tip,int &noSuspects)
{
    KeyedItem aItem;
    bool tipFound = false;
    string aString;
    //look at each of the characters attributes
    //to see if they are a match for the tip
    for (int asd = 0; asd < suspectIndex; asd++)
    {
        tipFound = false;
        if (suspectsArray[asd] != "")
        {
            passedTree.searchTreeRetrieve(suspectsArray[asd], aItem);

            //goes through each of the attributes of a character
            for (int iop = 1; iop < aItem.attributes.getLength() + 1; iop++)
            {
                aItem.attributes.retrieve(iop, aString);
                transform(aString.begin(), aString.end(), aString.begin(), toupper);
                if (aString == tip)
                {
                    tipFound = true;
                }
            }
            //if none of character's attribute match
            //remove them from the suspectArray
            if (tipFound == false)
            {
                suspectsArray[asd] = "";
                noSuspects -= 1;
            }
        }
    }
}   // end findTIP

//Function to find the perpetrator, if any
string INQUIRY(BinarySearchTree& passedTree)
{
    string codeName;        //string variable for inquiry code name
    string command;         //string variable for command input
    string aString;
    string checkSuspect;
    int noSuspects = suspectIndex;
    bool tipFound = false;


    cout << "OK, we are now conducting an inquiry." << endl;
    cout << endl << "Enter a Code Name for this Inquiry:";
    cout << setw(5) << " ";
    getline(cin, codeName);

    while (command != "ADD" && command != "QUIT" && command != "INQUIRY")
    {
        cout << "What would you like to do?" << endl;
        getline(cin, command);

        transform(command.begin(), command.end(), command.begin(), toupper);
        if (command == "TIP")
        {
            cout << "Enter Tip Info:" << setw(25) << " ";
            getline(cin, command);
            transform(command.begin(), command.end(), command.begin(), toupper);

            findTip(passedTree, command, noSuspects);

            //if there's only 1 suspect left, alert the user
            if (noSuspects == 1)
            {
                cout << "ALERT! That leaves only one \n"
                    << "suspect in the " << codeName << " inquiry:"
                    << setw(13) << " ";
                for (int k = 0; k < suspectIndex; k++)
                {
                    if (suspectsArray[k] != "")
                    {
                        cout << suspectsArray[k] << endl;
                    }
                }
            }
            else if (noSuspects == 0) //all suspects have been eliminated 
            {
                cout << "There no suspects that are match." << endl;
            }
        }
        else if (command == "CHECK")
        {
            bool found = false;
            cout << "Enter Name of character:" << setw(16) << " ";
            getline(cin, checkSuspect);
            transform(checkSuspect.begin(), checkSuspect.end(),
                checkSuspect.begin(), toupper);
            for (int p = 0; p < suspectIndex; p++)
            {
                aString = suspectsArray[p];
                transform(aString.begin(), aString.end(), aString.begin(), toupper);
                if (aString == checkSuspect)
                {
                    cout << aString << " is a suspect." << endl;
                    found = true;
                }
            }
            if (!found)
            {
                cout << checkSuspect << " is not a suspect." << endl;
            }
        }
        else if (command == "PRINT")
        {
            cout << "Current Suspects are:" << endl;
            for (int k = 0; k < suspectIndex; k++)
            {
                if (suspectsArray[k] != "")
                {
                    cout << suspectsArray[k] << endl;
                }               
            }
        }
        else if (command == "ADD" || command == "QUIT" || command == "INQUIRY")
        {
            return command;
        }
    }
}   // end INQUIRY
Havik IV
  • 193
  • 3
  • 12
  • Could you post a *complete* example? There's important stuff missing here, like your `#include`s and `using`s, for example. – Jeff Taylor Oct 17 '15 at 00:50

2 Answers2

1

My guess would be that your program is not finding a "/" where it expects to in the input stream, and therefore never sets finishRecord = true;. You probably should check for eof in your inner loop as well as the outer one.

vacuumhead
  • 333
  • 2
  • 13
  • Another thing I noticed is that if the file contains nothing, it will still go into infinite loop, even though the first if statement is supposed to prevent the while loops from running if there's nothing in the file. This behavior is only present on the mac I'm running the code on, not on my Windows. – Havik IV Oct 17 '15 at 00:26
  • could it be that I'm missing something, going from visual studio compiler to a g++ compiler? – Havik IV Oct 17 '15 at 00:27
  • @HavikIV That if statement doesn't check for what you think it checks. istream::peek (http://www.cplusplus.com/reference/istream/istream/peek/) returns next character in the stream. If stream is empty it returns EOF value. While the ios::eof (http://www.cplusplus.com/reference/ios/ios/eof/) checks if eofbit flag is set, and returns true if it is. Correct way to check if the stream is not empty (in the way you want, e.g. via peek) is described here: http://www.cplusplus.com/reference/string/char_traits/eof/ – Algirdas Preidžius Oct 17 '15 at 01:26
  • so i should make it like this? if (!(myfile.peek() == char_traits::eof())) – Havik IV Oct 17 '15 at 02:03
  • @HavikIV No. Discard entirely. `while (getline(myfile, name))` will do all the checking you should need. If the stream is invalid for any reason, including end of file, the loop will not enter. – user4581301 Oct 17 '15 at 02:13
  • You also need to check that `getline(myfile, aString);` is giving a valid result. – user4581301 Oct 17 '15 at 02:14
  • oh so i don't need the if statement – Havik IV Oct 17 '15 at 02:38
0

The reason this works when compiled with Visual Studio and not for the Mac is you are getting unlucky. You have a bug that makes it look like it's working on Windows, but the Mac is more fortunate and fails.

Starting with the readFile function because there's no point testing the rest of the program if it can't read the file correctly. Garbage in == garbage out.

 if (!myfile.peek() == myfile.eof())

Breaks down as int two chunks, the results of which are then compared.

!myfile.peek()

returns the read character or EOF on all errors. Then NOTs it. This results in false unless a NULL was read from the file.

myfile.eof()

Will always return true if the peek hit the end of the file, otherwise false.

So empty file case compares false against true should not enter the body of the if. No idea how you are getting an infinite loop here.

As for

while (!myfile.eof())

read Why is iostream::eof inside a loop condition considered wrong?

getline(myfile, name) returns a reference to the input stream and the input stream has a groovy operator bool() that returns true if the stream is readable, so

while (getline(myfile, name))

is actually all you need. If the file can't be read for any reason, including EOF, boom! The program is outta here.

That means all of the input can be simplified to:

while (getline(myfile, name))
{ // loop until read fails
    string aString;
    while (getline(myfile, aString) && aString != "/")
    { // loop until read fails or find end of record
        list1.insert(index, aString);
        index += 1;
    }
    KeyedItem an_item(name, list1, true);
    loadTree.searchTreeInsert(an_item);

    //reset variables used for each record
    index = 1;
    list1.removeAll();
}

Although there is a strong case to be made for trapping aString != "/"separately so the program can handle the file ending (or otherwise becoming unreadable) before the current record ends. The above code just wraps up and calls it a day as though nothing went wrong.

while (getline(myfile, name))
{ // loop until read fails
    string aString;
    while (getline(myfile, aString) && aString != "/")
    { // loop until read fails or find end of record
        list1.insert(index, aString);
        index += 1;
    }
    if (!myfile)
    {
        // handle error
    }

    KeyedItem an_item(name, list1, true);
    loadTree.searchTreeInsert(an_item);

    //reset variables used for each record
    index = 1;
    list1.removeAll();
}

Fills that gap.

Also, take advantage of Visual Studio's debugger to step through your code and see where things are going wrong. It will save you tonnes of time.

We do not have the code for the node or tree so we can't help much more. But once you know you have the data in and know it's correct (again use the debugger to peek at your nodes and the tree to make sure everything is right), you'll either be done or in great position to ask a new, much more specific question.

Community
  • 1
  • 1
user4581301
  • 29,019
  • 5
  • 26
  • 45