Reading from a text file

I'm working on a school project where I need to read data from a file. Each item in the file is separated by ';' to indicate the end of the preceding item. I have been able to read string data no problem but my program is tripping up when it attempts to read the int data into my array. What should I be using in order to read the integer data?

Sample .txt file:
Stereo Hearts;Gym Class Heroes;3;34;The Papercut Chronicles II
Counting Stars;OneRepulic;4;17;Native

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <fstream>
#include <cstring>

using namespace std;

//CONSTANTS
const int MAXCHAR = 300;
//STRUCT FOR A SONG SongType
struct SongType
{
	char song[MAXCHAR];
	char artist[MAXCHAR];
	int songMin;
	int songSec;
	char album[MAXCHAR];

};

//FORWARD DECLARATIONS
int readData(ifstream &inFile, SongType anArray[]);

int main()
{
	SongType myCollection[MAXCHAR];
	ifstream inFile;
	ofstream outFile;
	int songCount = 0;

	inFile.open("songs.txt");

	songCount = readData(inFile, myCollection);

	for (int i = 0; i < songCount; i++)
	{
		cout << myCollection[i].song << " "
			<< myCollection[i].artist << " "
			<< myCollection[i].songMin << " "
			<< myCollection[i].songSec << " "
			<< myCollection[i].album << endl;
	}
}
//FUNCTION TO READ songs.txt INTO STRUCT SongType
//As arguments takes infile, and SongType Array
//INPUTS EMPLOYEE FILE DATA INTO ARRAYS and returns the number of items in the array

int readData(ifstream &inFile, SongType anArray[])
{
	int counter = 0;
	while (!inFile.eof())
	{
		inFile.get(anArray[counter].song, MAXCHAR, ';');
		inFile.clear();
		inFile.ignore(MAXCHAR, ';');
		inFile.get(anArray[counter].artist, MAXCHAR, ';');
		inFile.clear();
		inFile.ignore(MAXCHAR, ';');

		//This is the part where I'm having the issue (I think...)
		inFile >> anArray[counter].songMin >> anArray[counter].songSec;

		inFile.get(anArray[counter].album, MAXCHAR, ';');
		inFile.clear();
		inFile.ignore(MAXCHAR, ';');
		counter++;
	}
	return counter;
}
inFile.get(anArray[counter].artist, MAXCHAR, ';');
When you are reading while specifying a delimiter, it is recommended that you use istream::getline instead of istream::get, it can potentially help you save lots of time debugging your code.

inFile.getline(anArray[counter].artist, MAXCHAR, ';');

Here is an example.
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <fstream>
#include <cstring>

using namespace std;

//CONSTANTS
const int MAXCHAR = 300;
//STRUCT FOR A SONG SongType
struct SongType
{
	char song[MAXCHAR];
	char artist[MAXCHAR];
	int songMin;
	int songSec;
	char album[MAXCHAR];

};

//FORWARD DECLARATIONS
int readData(ifstream &inFile, SongType anArray[]);

int main()
{
	SongType myCollection[MAXCHAR];
	ifstream inFile;
	ofstream outFile;
	int songCount = 0;

	inFile.open("songs.txt");

	songCount = readData(inFile, myCollection);

	for (int i = 0; i < songCount; i++)
	{
		cout << myCollection[i].song << " "
			<< myCollection[i].artist << " "
			<< myCollection[i].songMin << " "
			<< myCollection[i].songSec << " "
			<< myCollection[i].album << endl;
	}
}
//FUNCTION TO READ songs.txt INTO STRUCT SongType
//As arguments takes infile, and SongType Array
//INPUTS EMPLOYEE FILE DATA INTO ARRAYS and returns the number of items in the array

int readData(ifstream &inFile, SongType anArray[])
{
	char delimiter;
	int counter = 0;
	while (inFile)
	{

		// Stereo Hearts;Gym Class Heroes;3;34;The Papercut Chronicles II
		// Counting Stars;OneRepulic;4;17;Native

		inFile.getline(anArray[counter].song, MAXCHAR, ';');
		inFile.getline(anArray[counter].artist, MAXCHAR, ';');

		inFile >> anArray[counter].songMin >> delimiter;
		inFile >> anArray[counter].songSec >> delimiter;

		inFile.getline(anArray[counter].album, MAXCHAR);

		if(inFile) // Check if the reading operation is ok to avoid undefined bahavior
		{
			counter++;
		}
	}
	return counter;
}


Stereo Hearts Gym Class Heroes 3 34 The Papercut Chronicles II
Counting Stars OneRepulic 4 17 Native
The trick is to read the integer fields into a string and then use an istringstream to extract the integer. I made some other changes:
- readData() takes an istream instead of the more restrictive ifstream.
- I added readOne() to read a single record from a stream. This returns true/false depending on whether the record was successfully read.
- readData() now calls readOne to read a temp record. Only if that succeeds does it copy the record into the array. That way you won't end up with half a record in the array.
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <fstream>
#include <cstring>
#include <sstream>

using namespace std;

//CONSTANTS
const int MAXCHAR = 300;
//STRUCT FOR A SONG SongType
struct SongType
{
    char song[MAXCHAR];
    char artist[MAXCHAR];
    int songMin;
    int songSec;
    char album[MAXCHAR];

};

//FORWARD DECLARATIONS
int readData(istream & input, SongType anArray[]);

int
main()
{
    SongType myCollection[MAXCHAR];
    ifstream inFile;
    ofstream outFile;
    int songCount = 0;

    inFile.open("songs.txt");

    songCount = readData(inFile, myCollection);

    for (int i = 0; i < songCount; i++) {
	cout << myCollection[i].song << " "
	    << myCollection[i].artist << " "
	    << myCollection[i].songMin << " "
	    << myCollection[i].songSec << " " << myCollection[i].album << endl;
    }
}

bool
readOne(istream &is, SongType &result)
{
    // Sample: Stereo Hearts;Gym Class Heroes;3;34;The Papercut Chronicles II
    char ch;
    string str;
    istringstream ss;
    is.get(result.song, MAXCHAR, ';');
    is >> ch;
    is.get(result.artist, MAXCHAR, ';');
    is >> ch;

    getline(is, str, ';');
    ss.str(str);
    ss >> result.songMin;
    if (ss.fail()) return false;
    
    getline(is, str, ';');
    ss.str(str);
    ss.seekg(0);   // gotta reposition the stream to the beginning.
    ss >> result.songSec;
    if (ss.fail()) return false;
    
    getline(is, str);		// read the 
    strncpy(result.album, str.c_str(), sizeof(result.album));
    result.album[sizeof(result.album)-1] = 0; // null terminate if it's the exact length.

    return is.good();
}


//FUNCTION TO READ songs.txt INTO STRUCT SongType
//As arguments takes infile, and SongType Array
//INPUTS EMPLOYEE FILE DATA INTO ARRAYS and returns the number of items in the array

int
readData(istream & input, SongType anArray[])
{
    SongType tmp;
    int counter = 0;
    while (readOne(input, tmp)) {
	anArray[counter++] = tmp;
    }
    return counter;
}

Thank you both for the quick responses! This was very helpful, I'm going to try out both of your suggestions so I can understand both methods.

Mantorr22: After going through your solution I had some questions. I'm not sure I understand how these statements work:

while(inFile) and if(inFile)

I was using while(!inFile.eof()) to check if the loop has reached the end of the file but I don't understand how these statements accomplish the same thing.
Last edited on
Mantorr22, I wonder why someone reported our responses?

Bobbyjy, the advantage of checking while(inFile) istead of while(!inFile.eof()) is that the former will exit if there is a syntax error. Once a stream extraction operation fails, the stream is bad and no other extraction will succeed until the flags are cleared. That means it will never reach end-of-file.
Topic archived. No new replies allowed.