Basic Text File Code

Okay, so the problem I'm having is that I can't get it to read the file after I enter it. I'm assuming it's because the file isn't being opened correctly, but as I am completely new to this, I have no idea why..? Can anyone help me out?

:::::Code:::::

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

void menu(void);
void writeData(void);
void readData(void);
string * split(string, char);

const char delimiter = ',';
const char FileName[] = "c:/TestAddress.txt";
ofstream outMyAddress(FileName, ios::out);
ifstream MyAddress(FileName);
string name = " ";
string address = " ";
string city = " ";
string state = " ";
string zip = " ";
char response = 'Y';
int recordCount = 0;
string lineBuffer;
char userChoice = ' ';

int main ()
{
menu();
system("pause");
return 0;
} //end main

void menu(void)
{ //allow user to choose to append records, display records or exit the program
cout << "(A)ppend Records, (S)how Records, (E)xit" << endl;

cin >> userChoice;
userChoice = toupper(userChoice);

switch (userChoice)
{
case 'A':
case 'a': // append records
{
writeData();
}
break;

case 'S':
case 's': // read records
{
readData();
}
break;

case 'E':
case 'e': // exit program
{
cout << "Exiting Program..." << endl;
}
break;

default:
cout << "Invalid Choice" << endl;
break;
}
}//end menu

void writeData(void)
{
ifstream MyAddress;
cout << endl;
getline(cin, name);
cout << "Enter Name: ";
getline(cin, name);
cout << "Enter Address: ";
getline(cin, address);
cout << "Enter City: ";
getline(cin, city);
cout << "Enter State: ";
getline(cin, state);
cout << "Enter Zip: ";
getline(cin, zip);

outMyAddress << name << "," << address << "," << city << "," << state << "," << zip << endl;
outMyAddress.close();

cout << "\n Would you like to enter another record? (Y/N) ";
cin >> response;


while(response == 'y' || response == 'Y');
{
outMyAddress.close();
}
menu();

}
void readData(void)
{
ifstream MyAddress;
if(MyAddress.is_open()) // tests to see if file is open
{
while(!MyAddress.eof())
{
recordCount++;
getline (MyAddress, lineBuffer);
string * theFields[5] = {split(lineBuffer, ',')};

cout << " Record Number: " << recordCount << endl;
cout << "___________________________________________" << endl;
cout << "Name.........." << theFields[0] << endl;
cout << "Address.........." << theFields[1] << endl;
cout << "City.........." << theFields[2] << endl;
cout << "State.........." << theFields[3] << endl;
cout << "Zip.........." << theFields[4] << endl;
cout << "___________________________________________" << endl;
}
MyAddress.close();
}
menu();
}//end read data
ifstream MyAddress; should look like ifstream MyAddress("MyFile.txt");
Last edited on
Is ifstream the correct coding for read as well? If I try to run the readData function is just tosses me back to the menu. And If I do an if statement during the writeData function:

void writeData(void)
{

ifstream MyAddress("MyFile.txt");
MyAddress.open("MyFile.txt");
if(MyAddress.is_open())
{
cout << endl;
getline(cin, name);
cout << "Enter Name: ";
getline(cin, name);
cout << "Enter Address: ";
getline(cin, address);
cout << "Enter City: ";
getline(cin, city);
cout << "Enter State: ";
getline(cin, state);
cout << "Enter Zip: ";
getline(cin, zip);

outMyAddress << name << "," << address << "," << city << "," << state << "," << zip << endl;
outMyAddress.close();
}
else
{
cout << "Unable to open file";
}
cout << "\n Would you like to enter another record? (Y/N) ";
cin >> response;


while(response == 'y' || response == 'Y');
{
outMyAddress.close();
}
menu();

}

Then it just goes straight to the "else" factor of it and continues on. Clearly that means it's not opening... but I don't understand why if I tell it to open?

I have also tried using both of these:

ifstream MyAddress("MyFile.txt", ios::in);
MyAddress.open("MyFile.txt", ios::in);
Last edited on
One thing which is not recommended is the use of lots of global variables. In this case, the only ones I'd keep at the global scope are the two constants,
1
2
const char delimiter  = ',';
const char FileName[] = "c:/TestAddress.txt";


In general, it is better to declare variables where they are used, and limit their scope to the part of the code where they are needed.
The way in which the menu function operates looks a bit muddled too, as the called functions go on to recursively call the menu, which is unnecessary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void menu(void)
{
    // allow user to choose to append records, display records or exit the program
    char userChoice = ' ';

    while (userChoice != 'E')
    {
        cout << "(A)ppend Records, (S)how Records, (E)xit" << endl;

        cin >> userChoice;
        cin.ignore(1000, '\n');
        userChoice = toupper(userChoice);

        switch (userChoice)
        {
            case 'A':         // append records
                writeData();
                break;

            case 'S':         // read records
                readData();
                break;

            case 'E':         // exit program
                cout << "Exiting Program..." << endl;
                break;

            default:
                cout << "Invalid Choice" << endl;
                break;
        }
    }
}


Here's a way to write to the output file. Notice the two global constants are used, all other variables are declared locally where they will be used. std::strings will be initialised as an empty string:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void writeData(void)
{
    ofstream outMyAddress(FileName, ios::app);

    string name;
    string address;
    string city;
    string state;
    string zip;

    char response   = 'Y';
    while (response == 'Y' || response == 'y')
    {
        cout << "Enter Name: ";
        getline(cin, name);

        cout << "Enter Address: ";
        getline(cin, address);

        cout << "Enter City: ";
        getline(cin, city);

        cout << "Enter State: ";
        getline(cin, state);

        cout << "Enter Zip: ";
        getline(cin, zip);

        outMyAddress << name    << delimiter
                     << address << delimiter
                     << city    << delimiter
                     << state   << delimiter
                     << zip     << endl;

        cout << "\n Would you like to enter another record? (Y/N) ";
        cin >> response;
        cin.ignore(1000, '\n');
    }
}


And here's a simplified version of the function to read the file. (You will need to separate the individual fields too, this is just an example).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void readData(void)
{
    ifstream MyAddress(FileName);

    if (MyAddress.is_open()) 
    {
        string lineBuffer;
        int recordCount = 0;

        while(getline (MyAddress, lineBuffer))
        {
            recordCount++;
            cout << " Record Number: "  << recordCount " " << lineBuffer << endl;
        }
    }
}

Last edited on
Also, how do you input the code like that? rather than in pure textual form like I have been doing.

Alright it's now giving me the readData... kind of...

When it reads out the records, instead of the info I input, it shows

Name.... 00666AFC
Address... 00000000
City... 00000000
State... 00000000
Zip... 00000000

How would that happen?

-----------------
FULL CODE
-----------------


#include <iostream>
#include <fstream>
#include <string>
using namespace std;

void menu(void);
void writeData(void);
void readData(void);
string * split(string, char);

const char delimiter = ',';
const char FileName[] = "c:/TestAddress.txt";

int main ()
{
ofstream MyAddress;
menu();
system("pause");
return 0;
} //end main

void menu(void)
{ //allow user to choose to append records, display records or exit the program

char userChoice = ' ';

while(userChoice != 'E' || userChoice != 'e')
{
cout << "(A)ppend Records, (S)how Records, (E)xit" << endl;
cin >> userChoice;
userChoice = toupper(userChoice);

switch (userChoice)
{
case 'A':
case 'a':
writeData();
break;

case 'S':
case 's':
readData();
break;

case 'E':
case 'e':
cout << "Exiting Program..." << endl;
break;

default:
cout << "Invalid Choice" << endl;
break;
}
}
}//end menu

void writeData(void)
{
ofstream outMyAddress("MyFile.txt", ios::app);

string name = " ";
string address = " ";
string city = " ";
string state = " ";
string zip = " ";
char response = 'Y';

while(response == 'Y' || response == 'y')
{
getline(cin, name);
cout << "Enter Name: ";
getline(cin, name);

cout << "Enter Address: ";
getline(cin, address);

cout << "Enter City: ";
getline(cin, city);

cout << "Enter State: ";
getline(cin, state);

cout << "Enter Zip: ";
getline(cin, zip);

outMyAddress << name << delimiter
<< address << delimiter
<< city << delimiter
<< state << delimiter
<< zip << endl;

cout << "\n Would you like to enter another record? (Y/N) ";
cin >> response;
}


}
void readData(void)
{
ifstream MyAddress("MyFile.txt");
if(MyAddress.is_open()) // tests to see if file is open
{
string lineBuffer;
int recordCount = 0;

while(getline (MyAddress, lineBuffer))
{
recordCount++;
getline (MyAddress, lineBuffer);
string * theFields[5] = {split(lineBuffer, ',')};

cout << " Record Number: " << recordCount << endl;
cout << "___________________________________________" << endl;
cout << "Name.........." << theFields[0] << endl;
cout << "Address.........." << theFields[1] << endl;
cout << "City.........." << theFields[2] << endl;
cout << "State.........." << theFields[3] << endl;
cout << "Zip.........." << theFields[4] << endl;
cout << "___________________________________________" << endl;
}
MyAddress.close();
}
menu();
}//end read data




string * split(string theLine, char theDeliminator)
{
int splitCount = 0;

for(int i = 0; i < static_cast<int> (theLine.size()); i++)
{
if (theLine[i] == theDeliminator)
{
splitCount++;
}
}
splitCount++;

//create an array to hold the fields
string* theFieldArray;
theFieldArray = new string[splitCount];

//split the string into seperate fields
string theField = "";
int commaCount = 0;

for(int i = 0; i < static_cast<int> (theLine.size()); i++)
{ //read each character and look for the deliminator
if (theLine[i] != theDeliminator)
{
theField += theLine[i]; //build the field
}
else
{ //the deliminator was hit so save to the field to the array
theFieldArray[commaCount] = theField; //save the field to the array
theField = "";
commaCount++;
}
}
theFieldArray[commaCount] = theField;

return theFieldArray;
}
Last edited on
There are a few separate things here. First this, "Also, how do you input the code like that?".
Use code tags. Type the tags yourself like this,
[code]your code here[/code]
or (recommended), simply select the code and click the <> formatting button to the right of the editing window.

Now the program itself.

In function readData(), this line,
 
    string * theFields[5] = {split(lineBuffer, ',')};
has problems. It declares an array of pointers to strings. The first element, theFields[0] is assigned the address of the array returned by split(), the remaining four elements are initialised to zero.
Hence the code which displays the name and address details will output those values, which are pointers, so are displayed in hexadecimal as you stated above.

How to fix it? Well, instead of an array of pointers to strings, just declare a single pointer to strings, like this:
 
    string * theFields = split(lineBuffer, ',');


That is probably the biggest problem fixed, though there remain a few remaining bugs to be cleared up after that.

Other issues

The constant FileName is defined but never used. Instead, the file name is coded as a literal in two separate places in the code. The advantage of using a defined constant is that it guarantees that the value will be consistent throughout, and if you ever want to change it, it need be done only once, in an easy-to-find place at the top of the code, rather than having to scroll through the entire program looking for where it needs to be changed multiple times.

Similarly, the constant delimiter is used in function writeData() but a literal ',' is used instead in function readData(). Again, using the constant instead of a literal ensures consistency and makes it easy to change if need be.

In main(), ofstream MyAddress is defined but never used. It should be deleted.

In function menu() the user input is converted to upper case, thus checking for both 'E' and 'e', 'A' and 'a' etc. is unnecessary.

In function readData() there is an extra getline() inside the body of the loop, which causes the program to skip alternate lines from the file, and only display the even-numbered ones. That function is also calling the menu() function which is unnecessary and causes unwanted behaviour. Remove that line.

In function split(), the array is allocated dynamically using new[] but there is no corresponding delete[] to release that memory. This causes a memory leak. You can fix it by putting delete [] theFields; in function readData() just after the cout statements but still inside the while loop, so that for each call of function split() there is a matching delete [].

One more comment. The function split() seems to work, but it might be simplified by using a stringstream and getline with the required delimiter. That would neatly break the line at each comma in just a few lines of code.
Last edited on
After making all of the changes, It has gone back to not giving me anything for the readData function and simply putting me back at the menu (probably not opening the file in either writeData, readData, or both), but when I simply undid all the changes, it was still just giving me nothing...? I'm at a complete loss as to how I can go from getting an incorrect output to getting no output with the exact same line of code?
It probably is not opening the file either because the filename is incorrect, or the file itself has been removed.

The previous code had two different filenames,
 
    const char FileName[] = "c:/TestAddress.txt";
and
 
    ifstream MyAddress("MyFile.txt");


Make sure that if the file could not be opened, the program outputs some sort of error message, so that it is clear what is happening.
Last edited on
Topic archived. No new replies allowed.