PPP2 Chapter 11 Exercise 6

Exercise specifications:
Write a program that replaces punctuation with whitespace. Consider
. (dot), ; (semicolon), , (comma), ? (question mark), - (dash), ' (single quote) punctuation characters. Don’t modify characters within a pair of
double quotes ( " ). For example, “ - don't use the as-if rule. ” becomes
“ don t use the as if rule ”.


I currently have this 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
// chapter11ex6.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 8 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 6
// Exercise Specifications
/**
 * Write a program that replaces punctuation with whitespace. Consider
 * . (dot), ; (semicolon), , (comma), ? (question mark), - (dash), ' (single
 * quote) punctuation characters. Don’t modify characters within a pair of
 * double quotes ( " ). For example, “ - don't use the as-if rule. ” becomes
 * “ don t use the as if rule ”.
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <string>
#include <locale>
#include <algorithm>

int main()
{
	using namespace std;
	cout << "Please enter some words with punctuations (',-\".;?):\n";
	std::size_t i = 0;
	string input;
	getline(cin, input);
	string punct_chars{ ".;,?-'" };
	size_t index;
	for (char &ch : punct_chars)
	{
		index = input.find(ch);
	}
	for (size_t i = 0; i < input.size(); ++i)
	{
		cout << input[i];
		if (input[i] == input.find('"'))
		{
			size_t second = input.find('"');
			for (size_t j = 0; j < input[second]; ++j)
			{
				
			}
		}
		else
		{
			input.erase(index);
		}
	}
	cout << '\n';
	cin.clear();
	keep_window_open();
}


Current output with example input ("-don't use the as-if rule"):

Please enter some words with punctuations (',-".;?):
"-don't use the as-if rule"
"-don
Please enter a character to exit
f
Press any key to continue . . .
You need to rethink your logic.

30
31
32
33
	for (char &ch : punct_chars)
	{
		index = input.find(ch);
	}


Here, you loop over every character in punct_chars. Each time you find one of those characters in your input, you overwrite index with the index of the character you've just found. So, when your loop ends, you have a single value of index, which is the index of the last punctuation character you found.

In this case, that character is ' , because that comes later in punct_chars than -. So you exit that loop with index set to the position of that character.

Then you have a second loop:

34
35
36
37
38
39
40
41
42
43
44
45
	for (size_t i = 0; i < input.size(); ++i)
	{
		cout << input[i];
		if (input[i] == input.find('"')) // With the example string you're using, this is always false
		{
			// ...
		}
		else
		{
			input.erase(index);
		}
	}


Nowhere in your loop do you ever change the value of index. So, for every iteration of your loop, you're erasing the character at the same position in the input. I'm sure you can see how this results in erasing every character in your string from ' onwards?

EDIT: Also, your code would look nicer when posted here, if you configured whatever IDE/editor you're using so that, when it indents your code, it uses spaces rather than TAB characters.
Last edited on
Thanks for pointing that mistake out. So what should I do to index so that it's assigned the location of each character in punt_chars each time around the loop but doesn't go out of bounds of the string (should probably wrap back around instead (modulo?)).

Maybe I should put that range-based for loop iterating over punct_chars inside the other loop, and put the condition checking for whether or not the current character matches a character in that string inside that loop. I'll take out the index = input.find(ch); line out then. If that's a bad idea (putting a loop inside another loop could make the program lag too long, among other things), please suggest a good alternative it's not too much trouble. Thanks in advance.
First thing you should check if you are inside the quotation marks.
I would use a separate string for output.
I don't want to spoil the exercise so I just post a fragment of my solution I did a while ago.
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
for(;;)
{
    bool inQuot = false;
    string input, output;
    getline(cin, input);

    for (char ch : input)
    {
      if(ch == '"')
        inQuot = !inQuot;
      if (inQuot)
      {
        // handle char is inside quot
      }
      else
      {
        if (isPunct(ch))
        {
           // outside quotation we replace original char with a space
        }
        else
        {
          // not a puctuation so we add the  original char
        }
      }
    }
}
An infinite for loop (for (;;))? What condition should it end on?

Anyway, yeah, this is great. Thanks. I'll use that to fix mine.

Edit: Mine isn't replacing punctuations with spaces. I'll post the code here. [I also need help on putting characters into the output string if I do make a separate string for the output, and I'll have to see later how to change the indentation character in VS2015 from tab to spaces.]

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
// chapter11ex6.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 8 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 6
// Exercise Specifications
/**
 * Write a program that replaces punctuation with whitespace. Consider
 * . (dot), ; (semicolon), , (comma), ? (question mark), - (dash), ' (single
 * quote) punctuation characters. Don’t modify characters within a pair of
 * double quotes ( " ). For example, “ - don't use the as-if rule. ” becomes
 * “ don t use the as if rule ”.
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <string>
#include <locale>
#include <algorithm>

bool is_punct(const std::string &punct_chars, const char ch);

int main()
{
	using namespace std;
	cout << "Please enter some words with punctuations (',-\".;?):\n";
	cout << "(Enter Ctrl+Z on Windows or Ctrl+D on Linux to exit)\n";
	string punct_chars{ ".;,?-'" };
	for (;;)
	{
		bool quotes = false;
		string input;
		getline(cin, input);
		for (char ch : input)
		{
			if (ch == '"')
			{
				quotes = true;
			}
			if (quotes)
			{
				cout << ch;
			}
			else
			{
				if (is_punct(punct_chars, ch))
				{
					cout << ' ';
				}
				else
				{
					cout << ch;
				}
			}
		}
		if (cin.eof())
		{
			break;
		}
	}
	cout << '\n';
	cin.clear();
	keep_window_open();
}

bool is_punct(const std::string &punct_chars, const char ch)
{
	for (const auto &x : punct_chars)
	{
		if (ch == x)
		{
			return true;
		}
	}
	return false;
}
Last edited on
I found an easier solution. There is actually no real need to to have a separate string for output.
Modifying the input is actually easier.
To end the for(;;) loop you you to get a reply from the user if he wants to continue.

Another option for a loop is:
1
2
3
4
5
6
7
bool running = true;

  while(running)
  {
    // do stuff here
    running = confirm("Continue (y/n) ");
  }


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
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 6
// Exercise Specifications
/**
 * Write a program that replaces punctuation with whitespace. Consider
 * . (dot), ; (semicolon), , (comma), ? (question mark), - (dash), ' (single
 * quote) punctuation characters. Don’t modify characters within a pair of
 * double quotes ( " ). For example, “ - don't use the as-if rule. ” becomes
 * “ don t use the as if rule ”.
 */

#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

bool confirm(const string& msg)
{
  cout << msg << " ";
  char ch = cin.get();
  cin.ignore(255, '\n');

  return toupper(ch) == 'Y';
}

bool isPunct(char ch)
{
  static string punct = ".;,?-'";

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

int main()
{    
  bool inQuot = false;
  string input;

  for(;;)
  {
    getline(cin, input);
    for (char& ch : input)
    {
      if(ch == '"')
        inQuot = !inQuot;

      if (inQuot)
        continue; // nothing to do in quotes
      
      if (isPunct(ch))
        ch = ' '; // outside quotation we replace original char with a space
      
    } // for (char ch : input)
    
    cout << "After replace: " << input << "\n\n";

    if (!confirm("Continue (y/n) ")) // if not more we call it a day.
      break;
  } // end of for(;;)

  system("pause");
  return 0;

} // end of main
When I ran the above program in the Shell, there was no result from entering strings. And I had to stop it clicking the "Stop" button.

Mine doesn't replace periods after a closing double quote with spaces. So for example, the input
A wise man once said, "With great power comes great responsibility.".
is left completely unchanged in the output, including the second period (the one after the closing quote).

Here's my code (pretty similar to yours):
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
// chapter11ex6.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 8 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 6
// Exercise Specifications
/**
 * Write a program that replaces punctuation with whitespace. Consider
 * . (dot), ; (semicolon), , (comma), ? (question mark), - (dash), ' (single
 * quote) punctuation characters. Don’t modify characters within a pair of
 * double quotes ( " ). For example, “ - don't use the as-if rule. ” becomes
 * “ don t use the as if rule ”.
 */

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

bool is_punct(const char ch);
bool confirm(const std::string &message);

int main()
{
	using namespace std;
	cout << "Please enter some words with punctuations (',-\".;?):\n";
	string punct_chars{ ".;,?-'" };
	for (;;)
	{
		bool quotes = false;
		string input;
		getline(cin, input);
		for (char &ch : input)
		{
			if (ch == '"')
			{
				quotes = true;
			}
			if (quotes)
			{
				continue;
			}
			if (is_punct(ch))
			{
				ch = ' ';
			}
		}
		cout << "Result: " << input << '\n';
		if (!confirm("Continue (y/n)?"))
		{
			break;
		}
	}
	cin.clear();
	keep_window_open();
}

bool is_punct(const char ch)
{
	using namespace std;
	static const string punct_chars{ ".;,?-'" };
	for (const auto &x : punct_chars)
	{
		if (ch == x)
		{
			return true;
		}
	}
	return false;
}

bool confirm(const std::string &message)
{
	using namespace std;
	cout << message << " ";
	char ch = cin.get();
	cin.ignore(255, '\n');
	return toupper(ch) == 'Y';
}
Dragon, this bit of your instructions is contradicted by the example in the very next sentence:
Don’t modify characters within a pair of double quotes ( " ). For example, “ - don't use the as-if rule. ” becomes “ don t use the as if rule ”.
So you're saying periods after a closing double-quote are fine? Since I'm talking about the just the ones after a closing double-quote, not anything within a pair of double-quotes.
Last edited on
... punctuation characters. Don’t modify characters ...

given this context the second mention of 'characters' should refer to 'punctuation characters' in my opinion, so the sentence within quotes should remain unchanged. Then again I haven't seen the book and if you've copy/paste'd from the book then I suppose you can go with it though it still seems contradictory
It only says that everything within a pair of double quotes should remain unchanged, and that if it's not within a pair of double quotes, the following characters should be replaced with spaces: commas, question marks, hyphens, single quotes/apostrophes and semi-colons. But I guess the program doesn't know to change one of those characters into a space if it comes after a closing double quote. Should I tell it to do that, or should leave it be?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
   const string punct = ".;,?-'";
   string line;
   cout << "Input a line of text: ";   
   getline( cin, line );
   replace_if( line.begin(), line.end(), [&punct]( char c) { return punct.find( c ) != string::npos; }, ' ' );
   cout << line;
}


Input a line of text: " - don't use the as-if rule. "
"   don t use the as if rule  "


I suspect this is cheating, though.
It only says that everything within a pair of double quotes should remain unchanged

my point exactly but then see what happens to the following phrase that appears within double quotes :
“ - don't use the as-if rule. ”
in the example it becomes
“ don t use the as if rule ”

notice the inconsistency?
Hm, alright, point taken. I'll leave it be, 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
#include <iostream>
#include <string>

void transform_print( std::string str )
{
    static const std::string punct = ".;,?-'" ;

    bool in_quotes = false ;
    const auto translate = [&in_quotes]( char c )
    {
        if( c == '"' ) in_quotes = !in_quotes ;
        else if( !in_quotes && punct.find(c) != std::string::npos ) c = ' ' ;
        return c ;
    };

    for( char c : str ) std::cout << translate(c) ; // could use std::tranform here
    std::cout << '\n' ;
}

int main()
{
    std::string text ;
    while( std::cout << "enter a line of text: " && std::getline( std::cin, text ) )
        transform_print(text) ;
}

http://coliru.stacked-crooked.com/a/b297f61f39c06665
With the alternative interpretation of the double-quotey thing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

bool getRid( char c )
{
   static bool inQuotes = false;
   const string punct = ".;,?-'";
   if ( c == '"' ) inQuotes = !inQuotes;
   return !inQuotes && punct.find( c ) != string::npos;
}

int main()
{
   string line;
   cout << "Input a line of text: ";   
   getline( cin, line );
   replace_if( line.begin(), line.end(), getRid, ' ' );
   cout << line;
}


Input a line of text: Hello, this is an "over-the-top" example, isn't it?
Hello  this is an "over-the-top" example  isn t it 
@JLBorges: Thanks. Well, that seemed to work on the Shell here, but maybe I did something wrong on VS2015 that made it not work. Well, I'll leave it as it is. I've already marked this as solved, anyway.
Topic archived. No new replies allowed.