PPP2 Chapter 11 Exercise 3

The "disemvowels" exercise. Here are the specifications:

Write a program that removes all vowels from a file (“disemvowels”). For
example, Once upon a time! becomes nc pn tm! . Surprisingly often, the
result is still readable; try it on your friends.


The problem I'm having is with printing out a string at all (nothing gets printed). Here's the code:
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
// chapter11ex3.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 2 / 2017
// Bjarne Stourstrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 3
// Exercise Specifications:
/**
 * Write a program that removes all vowels from a file (“disemvowels”). For
 * example, Once upon a time! becomes nc pn tm!. Surprisingly often, the
 * result is still readable; try it on your friends.
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
#include <sstream>
#include <cctype>

bool is_vowel(const char ch);
void print_word(std::istream &is, std::string &word);

int main()
{
	using namespace std;
	cout << "Please enter input file name\n";
	string iname;
	cin >> iname;
	ifstream ifs{ iname };
	try
	{
		if (!ifs)
		{
			error("can't open input file", iname);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "runtime error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}

	string word;
	print_word(ifs, word);
	cout << '\n';
}

bool is_vowel(const char ch)
{
	bool is_vowel = true;
	char new_ch = ch;
	new_ch = tolower(ch);
	switch (new_ch)
	{
	case 'a':
	case 'e':
	case 'i':
	case 'o':
	case 'u':
		break;
	default:
		is_vowel = false;
		break;
	}
	return is_vowel;
}

void print_word(std::istream &is, std::string &word)
{
	using namespace std;
	size_t position = 0;
	while (getline(is, word))
	{
		if (!is_vowel(word[position]))
		{
			cout << word[position];
		}
		++position;
	}
	cout << '\n';
}


What am I doing wrong here?
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void print_word(std::istream &is, std::string &word)
{
        using namespace std;
        while (getline(is, word))
        {
           for ( size_t position = 0; position < word.size(); position++ )  //<==== need full loop on position
           {
                if (!is_vowel(word[position]))
                {
                        cout << word[position];
                }
           }
           cout << '\n';      //<==== line break inside the while
        }
}




Here's an alternative:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;

const string vowels = "aeiouAEIOU";

int main()
{
   string line;
   ifstream fin( "in.txt" );
   ofstream fout( "out.txt" );
   while ( getline( fin, line ) )
   {
      string::iterator iter = remove_if( line.begin(), line.end(), []( char c ){ return ( vowels.find( c ) != string::npos ); } );
      fout << line.substr( 0, iter - line.begin() ) << endl;
   }
   fin.close();
   fout.close();
}


When applied to the source code itself:
#ncld <strm>
#ncld <fstrm>
#ncld <strng>
#ncld <lgrthm>
sng nmspc std;

cnst strng vwls = "";

nt mn()
{
   strng ln;
   fstrm fn( "n.txt" );
   fstrm ft( "t.txt" );
   whl ( gtln( fn, ln ) )
   {
      strng::trtr tr = rmv_f( ln.bgn(), ln.nd(), []( chr c ){ rtrn ( vwls.fnd( c ) != strng::nps ); } );
      ft << ln.sbstr( 0, tr - ln.bgn() ) << ndl;
   }
   fn.cls();
   ft.cls();
}
Here is a simple demo:

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

bool isVowel(char ch)
{
  static const std::string vowels("aeiouAEIOU");

  return vowels.find(ch) != std::string::npos;
}

std::string removeVowels(const std::string& input)
{
  std::string output;

  for (const char ch : input)
    if (!isVowel(ch))
      output += ch;

  return output;
}

int main()
{
  std::ifstream src(__FILE__);

  for (std::string line; std::getline(src, line);)
  {
    std::string string_without_vowels = removeVowels(line);
    std::cout << string_without_vowels << "\n";
  }
  system("pause");
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main()
{
   string vowels = "aeiouAEIOU";
   char c;
   ifstream fin( "in.txt" );
   ofstream fout( "out.txt" );
   while ( fin.get( c ) ) if ( vowels.find( c ) == string::npos ) fout.put( c );
   fin.close();
   fout.close();
}


When applied to the source file:
#ncld <strm>
#ncld <fstrm>
#ncld <strng>
sng nmspc std;

nt mn()
{
   strng vwls = "";
   chr c;
   fstrm fn( "n.txt" );
   fstrm ft( "t.txt" );
   whl ( fn.gt( c ) ) f ( vwls.fnd( c ) == strng::nps ) ft.pt( c );
   fn.cls();
   ft.cls();
}

Last edited on
@lastchance: I took the first one. Thanks. Works like a charm.

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
// chapter11ex3.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 2 / 2017
// Bjarne Stourstrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 3
// Exercise Specifications:
/**
 * Write a program that removes all vowels from a file (“disemvowels”). For
 * example, Once upon a time! becomes nc pn tm!. Surprisingly often, the
 * result is still readable; try it on your friends.
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
#include <sstream>
#include <cctype>

bool is_vowel(const char ch);
void print_word(std::istream &is, std::string &word);

int main()
{
	using namespace std;
	cout << "Please enter input file name\n";
	string iname;
	cin >> iname;
	ifstream ifs{ iname };
	try
	{
		if (!ifs)
		{
			error("can't open input file", iname);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "runtime error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}

	string word;
	print_word(ifs, word);
	keep_window_open();
}

bool is_vowel(const char ch)
{
	bool is_vowel = true;
	char new_ch = ch;
	new_ch = tolower(ch);
	switch (new_ch)
	{
	case 'a':
	case 'e':
	case 'i':
	case 'o':
	case 'u':
		break;
	default:
		is_vowel = false;
		break;
	}
	return is_vowel;
}

void print_word(std::istream &is, std::string &word)
{
	using namespace std;
	size_t position = 0;
	while (getline(is, word))
	{
		for (size_t i = 0; i < word.size(); ++i)
		{
			if (!is_vowel(word[i]))
			{
				cout << word[i];
			}
		}
		cout << '\n';
	}
}


One thing I don't like here is that I can't make the string passed to my print_word() function const with the way I wrote the code. Everything else is fine.
One thing I don't like here is that I can't make the string passed to my print_word() function const with the way I wrote the code.


You are reading lines into word within print_word(). However, you could (probably more naturally) read each line in main() and send the line to print_word(). You have no further need to change it, so you can make it const std::string &word.
Using std::regex
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <string>
#include <regex>

int main()
{
    std::string input = "Once upon a time!";
    std::string output = std::regex_replace(input, std::regex("[aeiouAEIOU]"), "");
    std::cout << output << "\n";
}
How about 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
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
// chapter11ex3.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 2 / 2017
// Bjarne Stourstrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 3
// Exercise Specifications:
/**
 * Write a program that removes all vowels from a file (“disemvowels”). For
 * example, Once upon a time! becomes nc pn tm!. Surprisingly often, the
 * result is still readable; try it on your friends.
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
#include <sstream>
#include <cctype>

bool is_vowel(const char ch);
void print_word(const std::string &word);
void read_words(std::istream &is, std::string &word);

int main()
{
	using namespace std;
	cout << "Please enter input file name\n";
	string iname;
	cin >> iname;
	ifstream ifs{ iname };
	try
	{
		if (!ifs)
		{
			error("can't open input file", iname);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "runtime error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}

	string word;
	read_words(ifs, word);
	keep_window_open();
}

bool is_vowel(const char ch)
{
	bool is_vowel = true;
	char new_ch = ch;
	new_ch = tolower(ch);
	switch (new_ch)
	{
	case 'a':
	case 'e':
	case 'i':
	case 'o':
	case 'u':
		break;
	default:
		is_vowel = false;
		break;
	}
	return is_vowel;
}

void print_word(const std::string &word)
{
	using namespace std;
	for (size_t i = 0; i < word.size(); ++i)
	{
		if (!is_vowel(word[i]))
		{
			cout << word[i];
		}
	}
	cout << '\n';
}

void read_words(std::istream &is, std::string &word)
{
	using namespace std;
	while (getline(is, word))
	{
		print_word(word);
	}
}


It works and the design is also desirable.
Using std::regex

@gunnerfunner, he is following a book and regex will be covered at chapter 23.
Chapter 11 Exercise 3

Thomas - so some way to go then, good luck Dragon. Many might raise an eyebrow but I find Stourstrup very difficult to follow and sometimes unnecessarily compicated so I stay well clear of his literary works so to speak. But then others might point out it explains a lot about my C++ :))
Last edited on
It works and the design is also desirable.

Desirable to whom? In your is_vowel() function why did you use that switch case statement instead of a simple if statement. All of your case statements are empty so why even have them?


I'd lean more to something like:
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
#include <iostream>
#include <fstream>
#include <string>

bool is_vowel(char ch);  // Sending a copy so no need for the const qualifier.
void print_word(const std::string& word);
/* No sense to pass the string, you're resetting it several
times in the function and you never actually use the "returned" string anyway so make the
"string" local to this function instead. Pass an istream so you can print somewhere other than 
cout if you so desire. */ 
void read_words(std::istream& is);  

void keep_window_open() {}

int main()
{
    std::cout << "Please enter input file name\n";
    std::string iname;
    std::cin >> iname;

    std::ifstream ifs{ iname };
    // No neead to complicate things with the exceptions. Just print the message and return to the operating system.
    if(!ifs)
    {
        std::cerr << "Runtime Error: Can't open input file!\n";
        keep_window_open();
        return 1;
    }

    read_words(ifs);
    keep_window_open();

    return 0;
}

bool is_vowel(char ch)
{
    const std::string vowels{"aeiouAEIOU"};
    return(vowels.find_first_of(ch) != std::string::npos);
}

void print_word(std::ostream& fout, const std::string &word)
{
    for(auto& itr : word)
        if(!is_vowel(itr))
            fout << itr;

    fout << '\n';
}

void read_words(std::istream &is)
{
    std::string line;
    while(getline(is, line))
        print_word(std::cout, line);
}


Okay, like this, then:
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
// chapter11ex3.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 2 / 2017
// Bjarne Stourstrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 3
// Exercise Specifications:
/**
 * Write a program that removes all vowels from a file (“disemvowels”). For
 * example, Once upon a time! becomes nc pn tm!. Surprisingly often, the
 * result is still readable; try it on your friends.
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cctype>

bool is_vowel(const char ch);
void print_word(std::ostream &os, std::string &word);
void read_words(std::istream &is);

int main()
{
	using namespace std;
	cout << "Please enter input file name\n";
	string iname;
	cin >> iname;
	ifstream ifs{ iname };
	if (!ifs)
	{
		cout << "runtime error: can't open input file " << iname << '\n';
		keep_window_open();
		return 1;
	}
	read_words(ifs);
	keep_window_open();
}

bool is_vowel(const char ch)
{
	using namespace std;
	const string vowels{ "aeiouAEIOU" };
	return (vowels.find_first_of(ch) != string::npos);
}

void print_word(std::ostream &os, std::string &word)
{
	using namespace std;
	for (const char ch : word)
	{
		if (!is_vowel(ch))
		{
			os << ch;
		}
	}
	os << '\n';
}

void read_words(std::istream &is)
{
	using namespace std;
	string word;
	while (getline(is, word))
	{
		print_word(cout, word);
	}
}


Thanks for the help, anyway.

And Thomas, yeah, I agree with you about the complexity. Stroustrup stresses simplicity of code a lot, but he still has really hard-to-understand, complicated code in the book. I understand that the concepts used are simple, but his own coding style still isn't. It can also be hard to tell sometimes what part of some given code with a loop belongs inside the loop and what part doesn't, though I think I'll be able to tell more easily now with experience.
1
2
bool is_vowel(const char ch);
void print_word(std::ostream &os, std::string &word);


There is really no need to pass that single char as a const, your passing by value which means that any changes made in that function will be lost when the function returns anyway.

But you really should be passing word as a const, since you're passing this by reference any changes inadvertently made in the function would be reflected in the calling function.

IMO, you're overusing the using namespace std clause. While it is usually better to keep the use of the using clause local to functions, putting the using clause in every function is no better than using a more global using statement. In most of your functions you would be better off just properly scoping the one or two terms that might require scoping instead of the using statement. About the only place in this program I might use the using statement would be possibly main().

Lastly, you probably should either be writing your error messages to std::err rather than std::cout or if you use std::cout you should either use std::flush or std::endl to insure the stream is promptly flushed to the output device.


Topic archived. No new replies allowed.