• 0

[C++] Reading in an unknown amount of variables from a text file


Question

I wondering how to read in an unknown amount of variables from a text file. The first 2 variables of each line is always a string. After that there is an unknown amount of float or int values (at least 1 though).

For Example:

Angelina Jolie 14 67 34

Brad Pitt 12 13

Johnny Depp 2

Robert Plant 99 23 75 23 84

Bob Builder 12 13 85

How would I read that in? I'm pushing in to the back of an STL list. This is what I'm doing right now, but it doesn't accommodate more than one grade.

	while(!inData.eof())
	 {
	    inData >>firstName>>lastName>>grade;
		fullName=firstName + ' ' + lastName;
		section.push_back(student(fullName, grade));
	 }

Link to comment
Share on other sites

13 answers to this question

Recommended Posts

  • 0

I wondering how to read in an unknown amount of variables from a text file. The first 2 variables of each line is always a string. After that there is an unknown amount of float or int values (at least 1 though).

For Example:

Angelina Jolie 14 67 34

Brad Pitt 12 13

Johnny Depp 2

Robert Plant 99 23 75 23 84

Bob Builder 12 13 85

How would I read that in? I'm pushing in to the back of an STL list. This is what I'm doing right now, but it doesn't accommodate more than one grade.

	while(!inData.eof())
 {
 inData >>firstName>>lastName>>grade;
		fullName=firstName + ' ' + lastName;
		section.push_back(student(fullName, grade));
 }

One way to do it might be to read in the whole line, then parse through it using strtok, with SPACE as a delimiter. For the first two process as a string, then however many else, process as grades.

Link to comment
Share on other sites

  • 0

I'll personally have you shot & maimed if you don't use boost::spirit::qi.

This is probably for homework. You can't really use boost libraries for homework... Defeats the purpose of the homework. Though I could be wrong.

In case it isn't homework, yeah, always use boost and stl whenever possible.

Link to comment
Share on other sites

  • 0

I'd create a struct with String FirstName, String LastName, Vector<int> Grades and just make a list (or vector) containing your struct datatype.

Is this for the ACM-ICPC?

Link to comment
Share on other sites

  • 0

This is probably for homework. You can't really use boost libraries for homework... Defeats the purpose of the homework. Though I could be wrong.

In case it isn't homework, yeah, always use boost and stl whenever possible.

A std::string can't directly be used with strtok and even if you're now allowed to use the more complex boost libraries boost::tokenizer should still be allowed.

Link to comment
Share on other sites

  • 0

Using vector<int> is probably the best way to go. You can also create a linked list of <int> and keep adding to it as needed. Just remember, anything you read from a text file is read as a characters or strings of characters. So, if you need to use the actual values of those grades, they need to be converted to numbers. atoi() can be used to do that.

Link to comment
Share on other sites

  • 0

Here's my version. In the future, let us know the following:

A) whether this is homework

B) what limitations exist on "external" headers/libraries/etc

#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;

#include &lt;boost/lexical_cast.hpp&gt;
#include &lt;boost/algorithm/string.hpp&gt;
#include &lt;boost/algorithm/string/split.hpp&gt;

struct Homie
{
	string Name;
	vector&lt;float&gt; Grades;
};

int main()
{
	ifstream dataFile("data.txt");
	vector&lt;Homie&gt; homies;
	string tempLine;

	while(!dataFile.eof() &amp;&amp; std::getline(dataFile, tempLine))
	{		
		if (tempLine.length() &lt; 1)
		{
			cout &lt;&lt; "BARF: invalid line length: " &lt;&lt; tempLine &lt;&lt; endl;
			continue;
		}

		vector&lt;string&gt; splitLine;
		Homie tempHomie;

		boost::split(splitLine, tempLine, boost::is_any_of(" "), boost::token_compress_on);

		if (splitLine.size() &lt; 3)
		{
			cout &lt;&lt; "BARF: invalid line data: " &lt;&lt; tempLine &lt;&lt; endl;
			continue;
		}

		tempHomie.Name = splitLine[0] + " " + splitLine[1];

		int gradeCount = splitLine.size() - 2;

		cout &lt;&lt; "Found " &lt;&lt; gradeCount &lt;&lt; " grades for '" &lt;&lt; tempHomie.Name &lt;&lt; "'" &lt;&lt; endl;

		for (int i = 2; i &lt; splitLine.size(); i++)
		{
			try
			{
				tempHomie.Grades.push_back(boost::lexical_cast&lt;float&gt;(splitLine[i]));
			}
			catch(boost::bad_lexical_cast &amp;)
		    {
				cout &lt;&lt; "BARF: invalid grade: " &lt;&lt; splitLine[i] &lt;&lt; " for '" &lt;&lt; tempHomie.Name &lt;&lt; "'" &lt;&lt; endl;
		    }
		}

		homies.push_back(tempHomie);
	}

	if (dataFile.is_open())
	{
		dataFile.close();
	}

	cout &lt;&lt; "\n-----SUMMARY-----" &lt;&lt; endl;

	for (int i = 0; i &lt; homies.size(); i++)
	{
		cout &lt;&lt; homies[i].Name &lt;&lt; ":";

		for (int x = 0; x &lt; homies[i].Grades.size(); x++)
			cout &lt;&lt; " " &lt;&lt; homies[i].Grades[x];

		cout &lt;&lt; endl;
	}	

	return 0;
}

Link to comment
Share on other sites

  • 0

Nice one, boogerjones. Didn't know about boost::lexical_cast and the string split method.

Here's mine. It's standard library-only and the error checking is not as sophisticated as in boogerjones' version. It's more likely to comply with homework guidelines though. It uses lambdas so it requires a recent compiler like VS2010.

#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;sstream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
using namespace std;

struct Record {
	string FirstName;
	string LastName;
	vector&lt;float&gt; Grades;
};

int main() {
	fstream file("data.txt");
	if (!file.is_open()) {
		cerr &lt;&lt; "Could not find data.txt. Closing.\n";
		return -1;
	}

	// Read in records
	vector&lt;Record&gt; records;
	string line;
	while (!file.eof()) {
		getline(file, line);
		stringstream ss(line);
		ss.exceptions( ios::failbit | ios::badbit );
		records.push_back(Record());
		Record&amp; currentRecord = records.back();

		try {
			ss &gt;&gt; currentRecord.FirstName;
			ss &gt;&gt; currentRecord.LastName;
			while (!ss.eof()) {
				float grade;
				ss &gt;&gt; grade;
				currentRecord.Grades.push_back(grade);
			}
		}
		catch(ios::failure e) {
			cerr &lt;&lt; "Bad record detected. Skipping.\n";
			records.pop_back();
		}
	}

	// Display results
	for_each(records.begin(), records.end(), [] (Record&amp; record) { 
		cout &lt;&lt; record.FirstName &lt;&lt; " " &lt;&lt; record.LastName;
		for_each(record.Grades.begin(), record.Grades.end(), [] (float grade) {
			cout &lt;&lt; " " &lt;&lt; grade;
		});
		cout &lt;&lt; '\n';
	});
}

Edited by Dr_Asik
Link to comment
Share on other sites

  • 0

Nice one, boogerjones. Didn't know about boost::lexical_cast and the string split method.

Here's mine. It's standard library-only and the error checking is not as sophisticated as in boogerjones' version. It's more likely to comply with homework guidelines though. It uses lambdas so it requires a recent compiler like VS2010.

&lt;&lt;snipped&gt;&gt;

Nice, though you could try adding a few more comments, it may not be that clear to the OP

Link to comment
Share on other sites

  • 0

Nice, though you could try adding a few more comments, it may not be that clear to the OP

There ya go (a lot of newbie comments though, I wouldn't write that in "real" code):

#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;sstream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
using namespace std;

// A record is composed of a first name (string), a last name (string),
// and a variable number of grades (float).
struct Record {
	string FirstName;
	string LastName;
	vector&lt;float&gt; Grades;
};

int main() {
	// Step 1) Open file and verify that it was successful
	fstream file("data.txt");
	if (!file.is_open()) {
		cerr &lt;&lt; "Could not find data.txt. Closing.\n";
		return -1;
	}

	// Step 2) Read in records
	vector&lt;Record&gt; records;
	string line;

	// Loop until end-of-file found, that is, there is nothing else to read
	while (!file.eof()) { 
		// Read the next line into "line"
		getline(file, line); 
		// The stringstream allows us to easily extract white-space separated elements from the line
		stringstream ss(line); 
		// We set it so that it will throw on error rather than remain silent
		ss.exceptions( ios::failbit | ios::badbit ); 

		// Create a new entry in the vector of records, use it as the current entry
		// We will then fill it with data from the current line
		records.push_back(Record());
		Record&amp; currentRecord = records.back();

		try {
			// If anything goes wrong here, we jump to the catch block and then the main while loop continues
			ss &gt;&gt; currentRecord.FirstName; // Extract first name
			ss &gt;&gt; currentRecord.LastName; // Extract last name

			// Extract grades as floats until there are none remaining
			while (!ss.eof()) {
				float grade;
				ss &gt;&gt; grade;
				// Add each grade to the vector of grades of the current record
				currentRecord.Grades.push_back(grade);
			}
		}
		// This block is only entered if something went wrong
		catch(ios::failure e) {
			// the stringstream will throw an exception of type ios::failure if it cannot do what we asked for.
			// for example, if we try to extract a float but there are letters in it.
			cerr &lt;&lt; "Bad record detected. Skipping.\n";
			// remove the last record, we've just learned that it is invalid
			records.pop_back();
		}
	}

	// Step 3) Display results
	// We're just looping over the records and displaying each of them in turn.
	// Refer to http://cplusplus.com/reference/algorithm/for_each/ for the for_each algorithm 
	// and http://msdn.microsoft.com/en-us/library/dd293608.aspx for an overview of lambda expressions
        // if this syntax looks foreign to you.
	for_each(records.begin(), records.end(), [] (Record&amp; record) { 
		cout &lt;&lt; record.FirstName &lt;&lt; " " &lt;&lt; record.LastName;
		for_each(record.Grades.begin(), record.Grades.end(), [] (float grade) {
			cout &lt;&lt; " " &lt;&lt; grade;
		});
		cout &lt;&lt; '\n';
	});
}

Link to comment
Share on other sites

  • 0

1. Read a line into a buffer

1a. sscanf() the data into program variables, or use a strtok loop to parse them out.

2. Use fscanf().

Rinse and repeat until you encounter EOF.

That's what I would do in c. It's simple with very little overhead. It must be noted though, if you do use the scanf type functions, then you might need to put an upper limit in the format string on string types so you don't overflow, unless of course the data is crafted manually by you or you can guarantee the format, in which case, it should be safe without such checks.

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.