Help on a string program

Our teacher is very specific. He gave us a program that wants you to read in a data file with names and sort each one in a lastName,FirstName Middle method. We have not learned arrays, the for loop, the do while loop and many others yet. We have learned if/else, while, getline(), isspace (all the functions like that), and a few other things. I have tried writing code over and over and can't understand how I nest my loops to read this text file and then organize it. Basically some lines will say lastname and then you read the name after that then the other two names are the first. I made some code, but its not correct I know. I tried to see if I could call isspace and basically if name=="lastname" then skip a space and read that into lastname then else if name=="surname" read that into surname and then print out. He taught us today that if a character != '\n' then keep reading, but I can't get it to work.

Sample output file:
lastname williams kay elizabeth
surname bob taylor waters

He wants them in this format:
James, John Jim


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

using namespace std;

int main() 
{
    ifstream infile; //declaring the first file to open 
    ofstream outfile;
    
    infile.open("C:\\data\\namesLast.txt");
    if(!infile)
    {
        cout<<"Cannot read, please try again.";
        return 0;
    }
    
    string name;
    int count=0;

    while(count<30)
    {

        getline(infile,name, '\n');
        cout<<name<<endl;
        count++;
    }
    infile.close();
    return 0;
    
}


This is all I have so far and all it does is print the entire file as it was. I have deleted many other methods I have tried like while(count,30) if(name=="lastname" { if (isspace(dummy))...I just have no idea. Any guidance that I can use up to this point learned would be greatly appreciated.
Last edited on
You probably don't wanna use getline, since it'll be easier to see check each word coming in with cin. Notice each line has 4 words, so you know you've finished taking in the name of one person after you cin 4 times.

What you can do is simply input one word at a time. Assume the word you input is the first name unless the word happens to be "surname"/"lastname". In that case, you'll know to discard that word and input the next word as the last name.

Here's some overly exaggerated code to digest (to help you see what you can do):

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


using namespace std;

int main()
{
	ifstream infile; //declaring the first file to open 
	ofstream outfile;

	infile.open("namesLast.txt");
	if (!infile)
	{
		cout << "Cannot read, please try again.";
		return 0;
	}

	string name;
	string temp;
	string first;
	string middle;
	string last;

	while (infile >> temp)
	{
		if (temp == "lastname" || temp == "surname") //We took In The KeyWord
		{
			infile >> last; //Will Be The Last Name
			infile >> first; //Take In First name
			infile >> middle; // Take In Middle name
			name.append(last);
			name.append(", ");
			name.append(first);
			name.append(" ");
			name.append(middle);
			name.append("\n");
		}
		else //We Took In the First name
		{
			first = temp; 
			infile >> middle; //Take In Middle Name
			infile >> temp; //This Will Be The KeyWord - Will Discard
			infile >> last; //This Will Be The LAST Name
			name.append(last);
			name.append(", ");
			name.append(first);
			name.append(" ");
			name.append(middle);
			name.append("\n");
		}
	}
	cout << name << endl;
	infile.close();
	return 0;
}
Last edited on
What you can do is first to parse your string (with spaces as separator) in a vector like : {word1,word2,word3,word4}.

Then you identify which word is "lastName " or "surname".

After that, is you identified word3 as "surname" (for exemple), you must put all the words on the right of the word3 (here, only word4) into a string, and put the words on the left in another string. (Do the opposit if you found lastName instead of surname).

Then you should get something like :
- string lastnameHere
- string surnameHere

And so you can print it in the order you want.
Hey guys, we haven't learned vectors or name.append() yet and he would seriously mark off on my paper for that. He gave us some examples using character endline.

example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ifstream infile;

infile.open("numbers.txt")
if(!infile);
{
cout<<"error.";
return 0;
}

while(!infile.eof())
{
char khar;
s=0;
count=0;

while(khar!='\n)
{
infile>>v;
sum=sum+v;
count=count=1;
khar=infile.get();
}
average=sum/count; 


This is just an example and he hinted he wanted us to read each line and then look for a character turn to stop reading so I thought about putting that into a loop for 4 since there are 4 parts to a name and then saying if (name=="lastname") skip a space and then read the name if it equals "suername" then read that into surname..etc...he also wants us to capitalize each first letter and gave me this as an example:

lastname(0)=toupper(lastname(o))

I wish I could use arrays or anything else. Can't even use a for statement, no vectors or sort functions.
Last edited on
Surely you can do something like this:

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


using namespace std;

int main()
{
	ifstream infile; //declaring the first file to open 
	ofstream outfile;

	infile.open("namesLast.txt");
	if (!infile)
	{
		cout << "Cannot read, please try again.";
		return 0;
	}

	string name = "";
	string temp;
	string first;
	string middle;
	string last;

	while (infile >> temp)
	{
		if (temp == "lastname" || temp == "surname") //We took In The KeyWord
		{
			infile >> last; //Will Be The Last Name
			infile >> first; //Take In First name
			infile >> middle; // Take In Middle name
			name += last;
			name += ", ";
			name += first;
			name += " ";
			name += middle;
			name += "\n";
		}
		else //We Took In the First name
		{
			first = temp; 
			infile >> middle; //Take In Middle Name
			infile >> temp; //This Will Be The KeyWord - Will Discard
			infile >> last; //This Will Be The LAST Name
			name += last;
			name += ", ";
			name += first;
			name += " ";
			name += middle;
			name += "\n";
		}
	}
	cout << name << endl;
	infile.close();
	return 0;
}



Arrays would be overkill for simply parsing the text and displaying it.
Last edited on
zapshe, thank you! It worked! Now all I have to do is print to a file. I have one more question and I will leave you alone. In the code, he wanted us to capitalize the first letter of each name and he told us to call toupper, but I cannot get it to work and everything online I am seeing is saying another function that we haven't learned and that to upper only applies to characters.

his example that he wanted us to use for each part. He said the 0 is the first letter of the word and if you wanted to change the second letter it would be (1). I can't get it to work. My error says no match for call. I added the #include <cctype> and #include <ctype.h> just in case it needed either of those.
 
lastname(0)=toupper(lastname(0));


My textbook only shows toupper for a single character, not a string so I have no idea how to do it.
name[0] = toupper(name[0]); //one character … first letter of the name... right?
toupper is the right thing to use.

strings can be treated like arrays, as they overloaded the [] for you.
Last edited on
A string is an array of characters, so you can do as jonnin showed:

name[0] = toupper(name[0])

To get it Each first letter of each name capitalized, you'll want to instead use the toupper() function on the strings holding the names to make it easier. Once you take in the first name, capitalize the first letter, take in the middle name then capitalize the first letter, etc.. That way you can just add it onto the string and not worry about it.

I don't know why your professor gave you an example with () instead of []. If that's a way to access individual elements in the string then I've never seen it before.
Hello residentevil35,

For awhile I have dumbfounded by what you can and can not learn and what you can not use. And I have perplexed with what to say about it. Since I am not in the class I do not think it is my place to talk about something I do not know enough about.

Anyhow zapshe has a good solution that works, but here is a different solution. Also part of programming is to be able to take something like what zapshe has presented an change it to what you need. Sometimes parts of code are left out to give you something to work on.

This is similar to what zapshe has presented with some changes that you should fine usful.
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <cctype> // <--- std::tolower() and std::toupper().
#include <iostream> 
#include <iomanip>
#include <string>

#include <fstream>


//using namespace std;  // <--- Best not to use.

int main()
{
	const std::string PATH{ "C:\\data\\" };
	const std::string inFileName{ "namesLast.txt" };
	const std::string outFileName{ "Results.txt" };

	std::string temp1, temp2, temp3, temp4;

	 // <--- What you would use.
	//std::ifstream inFile(PATH + inFileName); //declaring the first file to open 
	//std::ofstream outFile(PATH + outFileName);

	// <--- What I use.
	std::ifstream inFile(inFileName); //declaring the first file to open 
	std::ofstream outFile(outFileName);

	//inFile.open(PATH + "namesLast.txt"); // <--- Can be used, but the above is better.

	if (!inFile)
	{
		std::cout << "\n File " << std::quoted(inFileName) << " did not open" << std::endl;
                //std::cout << "\n File \"" << inFileName << "\" did not open" << std::endl;

                //std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread". This line is optional.
		return 1;  //exit(1);  // If not in "main".
	}

	if (!outFile)
	{
		std::cout << "\n File " << std::quoted(outFileName) << " did not open" << std::endl;
		//std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread". This line is optional.
		return 2;  //exit(2);  // If not in "main".
	}

	std::string name;
	int count{}, badLines{};

	std::cout << '\n';

	while (inFile >> temp1 >> temp2 >> temp3 >> temp4)
	{
		//  <--- This should be in the while condition, but if used would require more work to break it up. 
		//std::getline(inFile, name, '\n'); // <--- The third parameter is not necessary as the "\n" is the default.

		if (temp1 == "lastname" || temp1 == "surname")
		{
			temp2[0] = std::toupper(temp2[0]);
			temp3[0] = std::toupper(temp3[0]);
			temp4[0] = std::toupper(temp4[0]);

			//if (temp2[0] >= 'a' && temp2[0] <= 'z')
			//	temp2[0] -= 32;

			//if (temp3[0] >= 'a' && temp3[0] <= 'z')
			//	temp3[0] -= 32;

			//if (temp4[0] >= 'a' && temp4[0] <= 'z')
			//	temp4[0] -= 32;

			name = temp2 + ", " + temp3 + ' ' + temp4;
		}
		else if (temp3 == "lastname" || temp3 == "surname")
		{
			temp1[0] = std::toupper(temp1[0]);
			temp2[0] = std::toupper(temp2[0]);
			temp4[0] = std::toupper(temp4[0]);

			name = temp4 + ", " + temp1 + ' ' + temp2;
		}
		else
		{
			std::cout << "\n Improper format. Please check the input file!" << std::endl;
			std::cout << "    " << temp1 << ' ' << temp2 << ' ' << temp3 << ' ' << temp4 << '\n' << std::endl;

			outFile << "\n Improper format. Please check the input file!" << std::endl;
			outFile << "    " << temp1 << ' ' << temp2 << ' ' << temp3 << ' ' << temp4 << '\n' << std::endl;

			badLines++;

			continue;
		}

		std::cout << ' ' << name << std::endl;

		outFile << name << std::endl;

		count++;
	}

	std::cout << "\n\n Processing finished.\n\n There were " << count << " lines in the input file.\n And "
		<< badLines << " inproperly formatted line(s) in the file." << std::endl;

	inFile.close();
	outFile.close();

	return 0;
}


To address what you said "<cctype>" and "<ctype.h>" are the same thing. The "ctype" is the C++ header file that should be used. Including both does not help or hinder you.

In your example: lastname(0)=toupper(lastname(0));) When accessing an array the []s are most often used although the ()s may work, but I am not sure about that part.

You are correct std::tolower(); and std::toupper(); only work on a single character. That is why you use lastname[0] to access the first element of the array and only work with one character.

At the beginning the first section of header files I do this way because the tend to be header files that are common to any program. By accident I found the putting them in alphabetical order helps you to remember and realize if you miss one.

The second section would be any header files that the given program would need. i.e., any extra header files.

The third section, if it was here, would be any header files that you would write, those that ate contained in double quotes. Although the order of the header files should not make a difference, sometimes it does.

I think you should understand the constant variables I defined.

For dealing with opening the file inFile.open(PATH + "namesLast.txt"); this works, but you can do the same thing with std::ifstream inFile(PATH + inFileName);. This not only sets up "inFile" as the handle to the file stream, but also opens the file at the same time.

The two if statements I use to check that the file stream is open and ready to use.

In the first if statement I have an alternative if you can not use "std::quoted()" from the "iomanip" header file. Change the comments as needed. Also you may have to change the if statement for the output if statement.

For an input file stream I would say it is mandatory that you check the stream status. With an output file stream it is more 50/50. If an output file name does not exist it will be created before it is written to, so not usually a problem. But when you use a path this can create a problem and can cause the output stream to fail. If you fail to check this the program will continue, but be unable to write to the file leaving you wondering why your file is empty.

I did the while statement a bit different. By reading all four at one time you can check what you need and arrange the variables in the proper order.

zapshe's method will work if you need to read one word at a time. Although I feel that the use of "+=" for everything is not necessary. Just as the "+=" is overloaded for the string class so too is the "+" operator overloaded to concatenate the string.

If you want some more input have a look at http://www.cplusplus.com/reference/string/string/ This will list all the member functions plus extras that are availavle to the string class.

I think that between the two programs you should be able to come up with something that will work.

In the future make mention that this is homework or a lab assignment along with what you can and can not use in the program. It is also helpful to include the directions that you were given for th program so that no one will have to guess at what the program needs to do. You did include the inpput file, but do not forget this in the future. And when you get to the point where you are creating your own header files do not forget to include them when you post your code. Unless it is a big program it is always better to post a program and all the files that can be compiled and run to test the program. Leaving something out just delays an answer.

Hope that helps,

Andy
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
#include <iostream> 
#include <fstream>
#include <sstream>
#include <string>
#include <cctype>
using namespace std;

int main() 
{
   string infilename  = "C:\\data\\namesLast.txt";
   string outfilename = "C:\\data\\names.out";
   
// ifstream in( infilename );
   stringstream in( "lastname Taylor marie denise\n"
                    "james wilis surname thomas\n"
                    "Stone Rock lastname Brown\n"
                    "surname lea high lee\n" );

// ofstream out( outfilename );
   ostream &out = cout;
   
   string line, result, part;
   while ( getline( in, line ) )
   {
      stringstream ss( line );
      result = "";
      while ( ss >> part )
      { 
         if ( part == "surname" || part == "lastname" )
         {
             ss >> part;
             part[0] = toupper( part[0] );
             result = part + ", " + result;
         }
         else
         {
             part[0] = toupper( part[0] );
             result += part + " ";
         }
      }
      out << result << '\n';
   }
}


Taylor, Marie Denise 
Thomas, James Wilis 
Brown, Stone Rock 
Lea, High Lee 
Last edited on
Thanks everyone for all your help. I was able to make the toupper function work as an array like you suggested. Maybe the teacher wrote it down with () and he meant [].

This is what I ended up doing.
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

        
        while (infile >> temp)
	{
		if (temp == "lastname" || temp == "surname") //We took In The KeyWord
		{
			infile >> last; //Will Be The Last Name
			infile >> first; //Take In First name
			infile >> middle; // Take In Middle name
                        middle[0]=toupper(middle[0]);
         first[0]=toupper(first[0]);
         last[0]=toupper(last[0]);
			name += last;
			name += ", ";
			name += first;
			name += " ";
			name += middle;
			name += "\n";
		}
		else //We Took In the First name
		{
			first = temp; 
			infile >> middle; //Take In Middle Name
			infile >> temp; //This Will Be The KeyWord - Will Discard
			infile >> last; //This Will Be The LAST Name
                        middle[0]=toupper(middle[0]);
                        first[0]=toupper(first[0]);
                        last[0]=toupper(last[0]);
			name += last;       
			name += ", ";
			name += first;
			name += " ";
			name += middle;
			name += "\n";
		}
                middle[0]=toupper(middle[0]);
         first[0]=toupper(first[0]);
         last[0]=toupper(last[0]);
	}
       
	outfile<<name<<endl;
        
 


and it ended up capitalizing the first letter of each word when I put it in front of the sorting first.

Last edited on
Yes, that's how I expected you to do it. Though honestly the code seems a bit inflated - this is only one way to do this.

I also fully agree with Handy Andy, professors seem to always want to remove some of the simplest of programming tools. I assume it's simply to try and make students think in some specific way, but I've only seen it make students write inefficient code and not fully delve into the language.


EDIT: You can replace those overly long "name += " statements with this:

name += (last + ", " + first + " " + middle + "\n");
Last edited on
If you are absolutely certain of the number of names and the possible positions of "surname" or "lastname" then here's a version with less repetition.

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
#include <iostream> 
#include <fstream>
#include <sstream>
#include <string>
using namespace std;

int main() 
{
   //ifstream infile( "C:\\data\\namesLast.txt" );
   stringstream infile( "lastname Taylor marie denise\n"
                        "james wilis surname thomas\n"
                        "Stone Rock lastname Brown\n"
                        "surname lea high lee\n" ); // Just for demonstration
   if( !infile )
   {
      cout << "Cannot read, please try again.";
      return 1;
   }

   //ofstream outfile("C:/data/sortedNames.txt");
   ostream &outfile = cout;                         // Just for demonstration

   string first, middle, last, temp;
   while ( infile >> temp )
   {
      if ( temp == "lastname" || temp == "surname" )
      {
         infile >> last >> first >> middle;   // read several at once
      }
      else
      {
         first = temp;
         infile >> middle >> temp >> last;
      }
      for ( char *c : { &first[0], &middle[0], &last[0] } ) *c = toupper( *c );    // repeated operation
      outfile << last << ", " << first << " " << middle << '\n';
   }   
}

Taylor, Marie Denise
Thomas, James Wilis
Brown, Stone Rock
Lea, High Lee
Topic archived. No new replies allowed.