The first day of class today we had an (optional) diagnostic programming assignment. The problem is: to take an input file with a sentence in it, and make all letters except the first one lowercase, make the first letter uppercase, and remove any extra spaces and line breaks. After working for awhile, and looking around for help, I have a basic idea of what I have to do, but so far it doesn't seem to be working.

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

ifstream in_stream;
string line;
char file_name_input[21];
char firstchar;
int main()
{

    cout << "Enter the file name of your input file (maximum of 20 characters):\n";
    cin >> file_name_input;
    in_stream.open(file_name_input);
    if (in_stream.fail( ))
    {
      cout << "Input file opening failed.\n";
      exit(1);
    }
    in_stream >> firstchar;
    //cout << firstchar;
    toupper(firstchar);
    cout << firstchar;
    
    if(in_stream.is_open())
      {
	while(in_stream)
	  {
	    getline(in_stream, line);
	    //tolower(line);
	    cout << line << endl;
	   
	  }
	  cout << line.size();
      }
  in_stream.close ( );
  return 0;
}

Right now, this is rather messy, but what I'm thinking is read in the first letter and convert it to uppercase using toupper, then read in the rest using getline, and use line.size() to determine the length of the string. Then, get the characters into an array and remove the spaces and then read back out. However, toupper and tolower are not working for me and line.size is not giving me the right number of characters. Please give me some comments, as I would like to understand these concepts before we get deeper into class. Thanks.

Recommended Answers

All 11 Replies

Try something like this.

string praseLine(const std::string& line){
 /* prase the line and return the prased line */
}
int main(){
 string prasedContent;
 string fileName("test.txt");
 ifstream fileIn(fileName.c_str());
 string line;
 while( getline(fileIn,line) ){
  string prasedLine =  praseLine(line);
  prasedContent += prasedLine;
 }
 doStuff(prasedContent);
}

toupper() is working fine. You just didn't store the result anywhere, throwing away the modified character.

As for line.size , what was input, what is the result, what were you expecting? Remember to give the details necessary for full understanding. Otherwise you get guesses and inadequate help.

How do you store a result from toupper?
The input for line.size is the string line, the result for it is an integer number, and I was expecting it to count every character in the line. I do need it to run every time getline runs, but when I put it in the next level loop, it causes an infinite loop.

You can use the STL std::transform() function to convert the whole line to lower case using a single line of code:

#include <iostream>
#include <string>
#include <algorithm>   // std::transform is in here

int main()
{
   /* Make a string, this is a tough one, with lots of things to trip your program up */
   std::string s("  The   QUICK  brown FoX jumpS Over thE  LAzy DOGs  \n HEllo");

   /* Output the original string */
   std::cout << s << std::endl;
   
   /* Convert everything to lower case with the tolower() function */
   std::transform(s.begin(),s.end(),s.begin(),tolower);

   /* output the string in lower case */
   std::cout << s << std::endl;

   return 0;
}

Then you need to find the first alpha-numeric character in s (using isalnum() ) and use toupper() on it to make it upper case. The string above starts with spaces, so just doing toupper(s.at(0)) will have no effect :o)

I wondered about using isalpha. Is there a way to make it all lowercase using tolower() because I have never seen some of the code you are using (if this were an assignment, I don't know that I would be allowed to use those). And how do you remove spaces?

On another note, how do you highlight the text in these forums?

I wondered about using isalpha. Is there a way to make it all lowercase using tolower()

tolower() just works on one char at a time, so you're always going to have to go through each element one-by-one to change them to lower case. The std::transform() function does this for you, but internally it's still going one at a time. You can see that it takes 3 arguments; the first tells is where to start working ( s.begin() in this case) and the second tells it where to stop ( s.end() ), the third argument tells it what to do to each of the elements that it looks at, in this case it calls the tolower() function on them. You could do the same thing with an iterator to the string, but it is a bit more long hand:

for(std::string::iterator it = s.begin(); it != s.end(); ++it)   *it = tolower(*it);

or, in a more C-like way:

for(unsigned i = 0; i < s.length(); i++)   s[i] = tolower(s[i]);

They're all doing the same thing: going through the string one character at a time and changing it to lower case.

On another note, how do you highlight the text in these forums?

Use the "icode" tag, it's not really highlighting, it's formatting the code in-line so that you can recognise it more easily when scanning through.

When I tried using std::transform , this is what I got:

no matching function for call to `transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unknown type>)'

I don't know what this means. I am using Bloodshed Dev C++ version 4.9.9.2 on Windows XP.

Also, for removing spaces, how would I go about that? My initial thought was to input the characters into an array and then go from there, but I don't think that's going to work or is going to be very difficult.

no matching function for call to `transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unknown type>)'

I think that the problem is the last argument, where it says <unknown type>. I don't know what it means, but it looks suspect.

I don't know what this means. I am using Bloodshed Dev C++ version 4.9.9.2 on Windows XP.

Sorry, I don't use that compiler. Maybe someone else has used this function with it?

Also, for removing spaces, how would I go about that? My initial thought was to input the characters into an array and then go from there, but I don't think that's going to work or is going to be very difficult.

There are loads of ways to do it. You probably don't need to use an array though. The simplest way is to go through the string and check each character and the next to see if they are both spaces. If they are, call std::string::erase() to get rid of it. This is quite inefficient, but will work.

The following is a common error:

#include <algorithm>
#include <iostream>
#include <string>
#include <cctype>

int main()
{
    std::string line;

    std::cout<<"Enter a line: ";

    if (getline(std::cin, line)) {
        std::cout<<"Before: "<< line <<'\n';

        // This is wrong!
        std::transform(line.begin(), line.end(), line.begin(), std::toupper);

        std::cout<<"After:  "<< line <<'\n';
    }
}

What's the problem? Unbeknownst to many, std::toupper is overloaded. There's the familiar version in <cctype> and another version in <iostream> with a second argument for the locale. So which of these is transform supposed to pick when you haven't specified the arguments? The answer is it's ambiguous, and shouldn't compile.

The following is correct:

#include <algorithm>
#include <iostream>
#include <string>
#include <cctype>

struct upper_case {
    int operator()(int c)
    {
        return std::toupper(c);
    }
};

int main()
{
    std::string line;

    std::cout<<"Enter a line: ";

    if (getline(std::cin, line)) {
        std::cout<<"Before: "<< line <<'\n';

        // This is correct
        std::transform(line.begin(), line.end(), line.begin(), upper_case());

        std::cout<<"After:  "<< line <<'\n';
    }
}

By creating a function or function object which calls the version you want explicitly, the ambiguity is removed. Alternatively, you can use the locale-friendly version:

#include <locale>

struct upper_case {
    int operator()(int c)
    {
        return std::toupper(c, std::locale());
    }
};
commented: Nice to know +7

What's the problem? Unbeknownst to many, std::toupper is overloaded. There's the familiar version in <cctype> and another version in <iostream> with a second argument for the locale. So which of these is transform supposed to pick when you haven't specified the arguments?

Excellent post! I haven't come across this before, my compiler (gcc, via Qt Creator) doesn't seen to complain about it, but I'll use this to resolve the ambiguity in the future :o)

The following is a common error:

#include <algorithm>
#include <iostream>
#include <string>
#include <cctype>

int main()
{
    std::string line;

    std::cout<<"Enter a line: ";

    if (getline(std::cin, line)) {
        std::cout<<"Before: "<< line <<'\n';

        // This is wrong!
        std::transform(line.begin(), line.end(), line.begin(), std::toupper);

        std::cout<<"After:  "<< line <<'\n';
    }
}

What's the problem? Unbeknownst to many, std::toupper is overloaded. There's the familiar version in <cctype> and another version in <iostream> with a second argument for the locale. So which of these is transform supposed to pick when you haven't specified the arguments? The answer is it's ambiguous, and shouldn't compile.

The following is correct:

#include <algorithm>
#include <iostream>
#include <string>
#include <cctype>

struct upper_case {
    int operator()(int c)
    {
        return std::toupper(c);
    }
};

int main()
{
    std::string line;

    std::cout<<"Enter a line: ";

    if (getline(std::cin, line)) {
        std::cout<<"Before: "<< line <<'\n';

        // This is correct
        std::transform(line.begin(), line.end(), line.begin(), upper_case());

        std::cout<<"After:  "<< line <<'\n';
    }
}

By creating a function or function object which calls the version you want explicitly, the ambiguity is removed. Alternatively, you can use the locale-friendly version:

#include <locale>

struct upper_case {
    int operator()(int c)
    {
        return std::toupper(c, std::locale());
    }
};

Thanks, didn't know that really. Under visual studio it doesn't complain, but www.codepad.org seems to complain, not sure what compiler they use. Narue, do you know what compiler reports this as an error? And looking at C++ reference it only reports one function and no overloaded function.

EDIT: Oh I see. The template version is probably included indirectly, and thus giving the ambiguous error.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.