Issue with strings and vectors

I'm trying to make a small program to convert a basic text file to XML without using any other libraries. It is for an assignment, for the record. I've tried different options, but am at a loss.

Here is an example of the file:
1
2
3
4
5
6
19936 WALKER KOLTON PORTLAND TN
HARMAN INTERNATIONAL INDUSTRIES INC
LUCENT TECHNOLOGIES INC
COMPUTER SCIENCES CORP
ECHOSTAR COMMUNICATIONS CORPORATION
--END_MANAGER_DATA--


What the output should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<accounts>
	<account>
		<manager employeeID="19936">
			<lastName>WALKER</lastName>
			<firstName>KOLTON</firstName>
		</manager>
		<location>
			<city>PORTLAND</city>
			<state>TN</state>
		</location>
		<companies count="4">
                        <company>HARMAN INTERNATIONAL INDUSTRIES INC</company>
                        <company>LUCENT TECHNOLOGIES INC</company>
                        <company>COMPUTER SCIENCES CORP</company>
                        <company>ECHOSTAR COMMUNICATIONS CORPORATION</company>
		</companies>
         </account>
</accounts>


What it does look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<accounts>
	<account>
		<manager employeeID="19936">
			<lastName>WALKER</lastName>
			<firstName>KOLTON</firstName>
		</manager>
		<location>
			<city>PORTLAND</city>
			<state>TN</state>
		</location>
		<companies count="5">
		</companies>
         </account>
</accounts>


When I run the program, it writes the information fine until it reaches the companies loop. After troubleshooting, I realize the issue is it isnt being fed into the vector properly. Advice?

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 <string>
#include <vector>

using namespace std;

int main() {
	//ask for input filename


	//Open files for input and output
	ifstream infile;
	infile.open("data.txt", ios::in);

	ofstream outfile;
	outfile.open("data.xml", ios::out);

	//declare local variables
	string endData = "--END_MANAGER_DATA--";
	int empNumber = 0;
	string lastName = "";
	string firstName = "";
	string city = "";
	string state = "";
	string company = "";
	vector <string> companies;
	
	//input the account info
		infile >> empNumber;
		infile >> lastName;
		infile >> firstName;
		infile >> city;
		infile >> state;
		getline(infile, company);
		cout << company;
		while (company != endData){
			companies.push_back(company);
			getline(infile, company);
		}
		
	//Writes the info to XML. While this section is blocky, it is for ease of viewing.
	outfile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
	outfile << "<!-- Processed by Brandon's converter -->" << endl;
	outfile << "<accounts>" << endl;
	outfile << "\t<account>" << endl;
	outfile << "\t\t<manager employeeID=\"" << empNumber << "\">" << endl;
	outfile << "\t\t\t<lastName>" << lastName << "</lastName>" << endl;
	outfile << "\t\t\t<firstName>" << firstName << "</firstName>" << endl;
	outfile << "\t\t</manager>" << endl;
	outfile << "\t\t<location>" << endl;
	outfile << "\t\t\t<city>" << city << "</city>" << endl;
	outfile << "\t\t\t<state>" << state << "</state>" << endl;
	outfile << "\t\t</location>" << endl;
	outfile << "\t\t<companies count=\"" << companies.size() << "\">" << endl;
	for (int i = 0; i < companies.size(), i++;) {
		outfile << "\t\t\t<company>" << companies[i] << "</company" << endl;
	}
	outfile << "\t\t</companies>" << endl;
	outfile << "\t<account>" << endl;
	outfile << "</accounts>";

	infile.close();
	outfile.close();

	system("pause");
	return 0;
}
You have cout on line 36. You could have another after line 39. What do they print?

Your line 55 writes "5", yet the following loop writes nothing. That is inconsistent.
Between line 34 and 35 there should be an ignore or get or something to read in the extra newline left in the buffer from operator >>. I would suggest putting something like
1
2
3
4
5
6
7
8
infile.ignore(1024, '\n');

//or

infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

//http://www.cplusplus.com/reference/istream/istream/ignore/
//http://www.cplusplus.com/reference/limits/numeric_limits/?kw=numeric_limits 
Last edited on
@Keskiverto It was to find out if the line was even being fed into the vector, thats all. I meant to delete it before I posted this.

@giblit That worked. I didn't even think of that. Appreciate it.

The second issue, however, is that the loop to access the vector isn't working properly. I haven't really messed with them much, but I thought I was using the right method of access, just like an array. It wont print anything to the file between the <company></company> tags.
Last edited on
int i = 0; i < companies.size(), i++; Here, you actually have i++ as your condition which checks if i is true (not 0) then increments. But, you set i equal to 0 first so it will never actually run. Maybe you meant for(int i = 0; i < companies.size(); ++i)
@giblit No, that isn't working either. I was doing the loop so it would step through and list the company for each element of the vector.
And that is what I showed you how to do :P There are other ways you could do it too such as using iterators or a ranged-based loop
1
2
3
4
for(std::string const &element : companies)
{
    outfile << "\t\t\t<company>" << element << "</company>" << endl;
}
Ha, I apologize. It is printing now, but out of the 4 companies, it only prints the second and fourth. Weird.
Show the current read loop and the print loop.
I might as well post the whole block just in case.

The read loop is line 37. The print loop is line 61.

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
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main() {
	//ask for input filename


	//Open files for input and output
	ifstream infile;
	infile.open("data.txt", ios::in);

	ofstream outfile;
	outfile.open("data.xml", ios::out);

	//declare local variables
	string endData = "--END_MANAGER_DATA--";
	int empNumber = 0;
	string lastName = "";
	string firstName = "";
	string city = "";
	string state = "";
	string company = "";
	vector <string> companies;
	
	//input the account info
		infile >> empNumber;
		infile >> lastName;
		infile >> firstName;
		infile >> city;
		infile >> state;
		infile.ignore(1024, '\n');
		getline(infile, company);
		while (company != endData){
			getline(infile, company);
			companies.push_back(company);
			getline(infile, company);
		}
		
	//Writes the info to XML. While this section is blocky, it is for ease of viewing.
	outfile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
	outfile << "<!-- Processed by Brandon's converter -->" << endl;
	outfile << "<accounts>" << endl;
	outfile << "\t<account>" << endl;
	outfile << "\t\t<manager employeeID=\"" << empNumber << "\">" << endl;
	outfile << "\t\t\t<lastName>" << lastName << "</lastName>" << endl;
	outfile << "\t\t\t<firstName>" << firstName << "</firstName>" << endl;
	outfile << "\t\t</manager>" << endl;
	outfile << "\t\t<location>" << endl;
	outfile << "\t\t\t<city>" << city << "</city>" << endl;
	outfile << "\t\t\t<state>" << state << "</state>" << endl;
	outfile << "\t\t</location>" << endl;
	outfile << "\t\t<companies count=\"" << compNum << "\">" << endl;
	/*for (int i = 0; i < compNum, i++;) {
		cout << companies[i] << endl;
		outfile << "\t\t\t<company>" << companies[i] << "</company" << endl;
	}*/
	for (std::string const &element : companies)
	{
		outfile << "\t\t\t<company>" << element << "</company>" << endl;
	}
	outfile << "\t\t</companies>" << endl;
	outfile << "\t<account>" << endl;
	outfile << "</accounts>";

	infile.close();
	outfile.close();

	system("pause");
	return 0;
}
You read too much. (Two lines per iteration.)
Lets do more:
1
2
3
4
5
infile.ignore(1024, '\n');
while ( getline(infile, company) && endData != company )
{
  companies.push_back( company );
}

Reads can fail. Now we test some.


Note1: Line 20, use const. You will not change endData, so you should be explicit.

Note2: Line 61, if you have range-for, then you have auto too. for ( auto const & element : companies )
Sure, it is less explicit and you have to trace back to figure out the value_type of companies during code revision, but it is still a neat feature.

Note3: Lines 13-17 and 69-70.
1
2
ifstream infile( "data.txt" ); // in by default, open on construction
ofstream infile( "data.xml" ); // out by default, open on construction 

Destructor of fstream closes open file, so explicit call to close() is not necessary.
Long evening at work. I will test this later today.
Everything is fine except for the fact that it is skipping the first company entry. Is this because the ignore size is too large?

Edit: I believe the loop was the initial problem. I commented out the ignore line and it works fine.
Last edited on
You probably are still reading before the loop. Instead of only in the loop.
Everything is golden except for one issue, one that doesnt make sense to me.

I changed it to read multiple entries:

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
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main() {

	//ask for input filename
	string textFile = "";
	string xmlFile = "";
	cout << "Please enter the name of the text file: ";
	cin >> textFile;
	cout << "Please enter the name of the generated XML file: ";
	cin >> xmlFile;

	//Open files for input and output
	ifstream infile(textFile + ".txt");
	ofstream outfile(xmlFile + ".xml");

	//declare local variables
	const string endData = "--END_MANAGER_DATA--";
	int empNumber = 0;
	string lastName = "";
	string firstName = "";
	string city = "";
	string state = "";
	string company = "";
	vector <string> companies;
	int compNum = 0;

	outfile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
	outfile << "<accounts>" << endl;	

	while (!infile.eof())
	{
		companies.clear();

		//input the account info
		infile >> empNumber;
		infile >> lastName;
		infile >> firstName;
		infile >> city;
		infile >> state;
		getline(infile, company);
		while (getline(infile, company) && endData != company){
			companies.push_back(company);
		}

		//Writes the info to XML. While this section is blocky, it is for ease of viewing.
		outfile << "\t<account>" << endl;
		outfile << "\t\t<manager employeeID=\"" << empNumber << "\">" << endl;
		outfile << "\t\t\t<lastName>" << lastName << "</lastName>" << endl;
		outfile << "\t\t\t<firstName>" << firstName << "</firstName>" << endl;
		outfile << "\t\t</manager>" << endl;
		outfile << "\t\t<location>" << endl;
		outfile << "\t\t\t<city>" << city << "</city>" << endl;
		outfile << "\t\t\t<state>" << state << "</state>" << endl;
		outfile << "\t\t</location>" << endl;
		outfile << "\t\t<companies count=\"" << companies.size() << "\">" << endl;
		for (string const &element : companies){
			outfile << "\t\t\t<company>" << element << "</company>" << endl;
		}
		outfile << "\t\t</companies>" << endl;
		outfile << "\t<account>" << endl;
	}

	outfile << "</accounts>";

	infile.close();
	outfile.close();

	system("pause");
	return 0;
}


The text file is as follows:

1
2
3
4
5
6
7
8
9
10
11
19936 WALKER KOLTON PORTLAND TN
HARMAN INTERNATIONAL INDUSTRIES INC
LUCENT TECHNOLOGIES INC
COMPUTER SCIENCES CORP
ECHOSTAR COMMUNICATIONS CORPORATION
--END_MANAGER_DATA--
79122 THOMPSON MARISSA ATLANTA GA
QUINTILES TRANSNATIONAL
BANTA CORPORATION
SEMPRA ENERGY
--END_MANAGER_DATA--


This is my output, oddly enough:

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
<?xml version="1.0" encoding="UTF-8"?>
<accounts>
	<account>
		<manager employeeID="19936">
			<lastName>WALKER</lastName>
			<firstName>KOLTON</firstName>
		</manager>
		<location>
			<city>PORTLAND</city>
			<state>TN</state>
		</location>
		<companies count="4">
			<company>HARMAN INTERNATIONAL INDUSTRIES INC</company>
			<company>LUCENT TECHNOLOGIES INC</company>
			<company>COMPUTER SCIENCES CORP</company>
			<company>ECHOSTAR COMMUNICATIONS CORPORATION</company>
		</companies>
	<account>
	<account>
		<manager employeeID="79122">
			<lastName>THOMPSON</lastName>
			<firstName>MARISSA</firstName>
		</manager>
		<location>
			<city>KINGSPORT</city>
			<state>TN</state>
		</location>
		<companies count="3">
			<company>QUINTILES TRANSNATIONAL</company>
			<company>BANTA CORPORATION</company>
			<company>SEMPRA ENERGY</company>
		</companies>
	<account>
	<account>
		<manager employeeID="79122">
			<lastName>THOMPSON</lastName>
			<firstName>MARISSA</firstName>
		</manager>
		<location>
			<city>KINGSPORT</city>
			<state>TN</state>
		</location>
		<companies count="0">
		</companies>
	<account>
</accounts>


It's reading the second entry twice? Im confused as to why the loop would even go back to the middle.
It's not reading the second stuff twice, it's not finding the end of file correctly. Unfortunately I can't seem to find why it is not.

A few things I notice though:
1) line 36 only checks if it doesn't reach end of file, what if something else erroneous happened? I would do while(infile) instead.

2) line 38 could be removed if you moved line 30 there.

3) line 46 would probably be better if it was infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

4) line 66 shouldn't it be /accounts instead of accounts? outfile << "\t</account>" << endl;

5) line 71 and 72 aren't necessary the destructor will call these for you.

6) System can lead to security issues to keep the console open please refer to http://www.cplusplus.com/forum/beginner/1/

7) You shouldn't include an entire namespace into the global scope it can lead to naming conflicts.

8) You should probably avoid using operator >> and getline for input. I would just use getline.
I appreciate the help you two have been giving me. I will tweak this all tomorrow, and try to figure out why it isn't ending properly.
Input validation (almost) all the way:
1
2
3
4
5
6
7
	while ( infile >> empNumber >> lastName >> firstName >> city >> state )
	{
		companies.clear();
		infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
		while (getline(infile, company) && endData != company){
			companies.push_back(company);
		}


Regarding (2), creating the vector outside the loop allows it to retain its capacity and therefore decreases the number of reallocations that the push_backs are bound to induce. A micro-optimization. One could go one step further with it and reserve() some sane amount before the loop.
I still can't figure out why it isn't finding the eof right.
Topic archived. No new replies allowed.