trim vowels

This program should take a word as an input,
get rid of the vowels and if there are two like consonants next
to each other, one of the two gets erased.
In case the outcome without the vowels and double consonants is more than 5 characters, the output should display the first 3 and last two characters only.
The function called 'long message' has indeed some mistakes, as some lines are underlined in red, and the last function has some underlining too.
If possible, I would like my code to be corrected instead of being totally rewritten.

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
  string is_consonant(string sec_message)
{
	
	for (int i = 0; sec_message[i] != '\0'; i++)
	{
		if (sec_message[i] == ('a') || sec_message[i] == ('e') || sec_message[i] == ('i') 
			|| sec_message[i] == ('o') || sec_message[i] == ('u'))
		{
			sec_message[i] == ' ';
		}

		if (sec_message[i] == sec_message[i + 1])
		{
			sec_message[i + 1] == '\0';
		}
	}
	return sec_message;
}

void long_message()
{
	if (is_consonant.length() > 5)
	{
		cout<< is_consonant.substr(0,2)<< 
			is_consonant.Substring(is consonant.Length - 3, 3);
	}
}


int main()
{
	char answer;
		do {
			string enter_word;
			cout << "enter a word" << endl;
			cin >> enter_word;

			long_message(enter_word);
			cout << "try again(Y/N)?" << endl;
			cin >> answer;
		} while (answer == 'Y' || answer == 'y');

		return 0;
}
The function called 'long message' has indeed some mistakes, as some lines are underlined in red, and the last function has some underlining too.

That's hardly a useful problem description. If your IDE or compiler are giving you error messages, then tell us what, and where, they are.
Let's start small. Read http://www.cplusplus.com/doc/tutorial/functions/

1. Do you know how to call a function?
2. Do you know how to call a function and pass parameters into it?

For example, you define a function called long_message(), which takes in no arguments, but then you attempt to call it like: long_message(enter_word);

If you want your long_message function to take in a string, you need to say that in your code.
1
2
3
4
void long_message(string word)
{

}
Last edited on
I corrected the "long message" function and now it works.
However I am now dealing with another problem.

The output only takes into account the second function and ignores the first.
In other words, the vowels are not ignored.

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

using namespace std;

string is_consonant(string sec_message)
{
	
	for (int i = 0; sec_message[i] != '\0'; i++)
	{
		if (sec_message[i] == ('a') || sec_message[i] == ('e') || sec_message[i] == ('i') 
			|| sec_message[i] == ('o') || sec_message[i] == ('u'))
		{
			sec_message[i] == '\0';
		}

		if (sec_message[i] == sec_message[i + 1])
		{
			sec_message[i + 1] == '\0';
		}
	}
	return sec_message;
}

void long_message(string word)
{
	if (is_consonant(word).length() > 5)
	{
		cout<< is_consonant(word).substr(0,2)<< 
			is_consonant(word).substr(is_consonant(word).length() - 3, 3);
	}
}


int main()
{
	char answer;
		do {
			string enter_word;
			cout << "enter a word" << endl;
			cin >> enter_word;

			long_message(enter_word);
			cout << "============" << endl<<endl;
			cout << "try again(Y/N)?" << endl;
			cin >> answer;
		} while (answer == 'Y' || answer == 'y');

		return 0;
}
Using line numbers from your last message:

Lines 14 and 19 do nothing. Remember that == compares the arguments and = assigns the right side to the left side. So these lines do a comparison and throw away the result.

You could jus change == to =, but then line 19 will still be wrong. Consider what happens when you hit it. Let's say that i=12 when the line is executed.
- it would set sec_message[13] = '\0';
- The loop repeats, so it increments i to 13 and then compares sec_message[13] to '\0'
- Since we just set sec_message[13] = '\0', the loop exits.

To fix this, first decide what should is_consonant() do? Decide that and write it as a comment before the function.

Now code it to do what it should do.

Writing the purpose as a comment helps to keep you focused on the problem at hand. It also works great to document your code and it can help you use meaningful names (hint: "is_consonant" doesn't really describe what this function does).
I have here changed the function name and switched == to =.
I know you said that 19 would still not work, but I cannot understand why, despite your explanation.
However, I have included some comments too.
The logical error that I get, concerns the output,
and the input is not filtered out as it should.

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

using namespace std;

string erase_vowels(string sec_message)
{
	
	for (int i = 0; sec_message[i] != '\0'; i++) // erase all vowels
	{
		if (sec_message[i] == ('a') || sec_message[i] == ('e') || sec_message[i] == ('i') 
			|| sec_message[i] == ('o') || sec_message[i] == ('u'))
		{
			sec_message[i] = '\0';
		}

		if (sec_message[i] == sec_message[i + 1]) //if two adjacent consonants are the same
		{                                          // erase one
			sec_message[i + 1] = '\0';
		}
	}
	return sec_message;
}

void long_message(string word) // if the typed word without the vowels and without double consonants
{                              // is longer than 5, then only the first two and last three letters 
	                           //should be the output
	if (erase_vowels(word).length() > 5)
	{
		cout<<erase_vowels(word).substr(0,2)<< 
			erase_vowels(word).substr(erase_vowels(word).length() - 3, 3);
	}
}


int main()
{
	char answer;
		do {
			string enter_word;
			cout << "enter a word" << endl;
			cin >> enter_word;

			long_message(enter_word);
			cout << "============" << endl<<endl;
			cout << "try again(Y/N)?" << endl;
			cin >> answer;
		} while (answer == 'Y' || answer == 'y');

		return 0;
}
The logical error that I get, concerns the output,
and the input is not filtered out as it should.
When this happens, please post an example of the input, the output you receive, and the output that you expect. It really helps us to help you.

Right now you only generate output when the word is long. Let's fix that by moving the output to the main program and having long_message return the modified message:
1
2
3
4
5
6
7
8
9
10
string
long_message(string word)       // if the typed word without the vowels and without double consonants
{                                                // is longer than 5, then only the first two and last three letters
    //should be the output
    string result = erase_vowels(word);
    if (result.length() > 5) {
        result = result.substr(0, 2) + result.substr(result.length() - 3, 3);
    }
    return result;
}

and in main
1
2
        string result = long_message(enter_word);
        cout << "result is " << result << '\n';


Now here is the output of a run:
$ ./foo
enter a word
aabb
result is b
============

try again(Y/N)?
y
enter a word
bbcc
result is bcc
============

try again(Y/N)?
n


Notice that the output for input bbcc is wrong. This is the "line 19" bug that I mentioned.

This might help. Here is erase_vowels() with some debug messages:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
string
erase_vowels(string sec_message)
{
    cout << "Entered erase_vowels with " << sec_message << '\n';
    int i;
    for (i = 0; sec_message[i] != '\0'; i++) // erase all vowels
    {
        if (sec_message[i] == ('a') || sec_message[i] == ('e') || sec_message[i\
] == ('i')
            || sec_message[i] == ('o') || sec_message[i] == ('u')) {
            sec_message[i] = '\0';
        }

        if (sec_message[i] == sec_message[i + 1])       //if two adjacent conso\
nants are the same
        {                                        // erase one
            cout << "setting index " << i+1 << " to \\0\n";
            sec_message[i + 1] = '\0';
        }
    }
    cout << "exited the loop because index " << i << " is \\0\n";
    return sec_message;
}


1
2
3
4
5
6
7
8
9
10
11
12
And here is a run with that code:
$ ./foo
enter a word
bbcc
Entered erase_vowels with bbcc
setting index 1 to \0
exited the loop because index 1 is \0
result is bcc
============

try again(Y/N)?
n


See if you can fix these problems. Then there's one more, that's actually more serious. You are changing the duplicate characters to 0. This doesn't actually erase them, it just makes them ASCII 0. You need to actually remove the characters instead.
I made a few changes according to your suggestions.
The output now keeps a vowel in case there is a double vowel,
even though all vowels should be erased.
Also, the last function outputs two different outcomes,
while I am trying to obtain only one right result.

for instance:
when I input: aabbccddeeff
I should be getting: bcdf

instead I get:


abdef
the secret code is :abcdef




here is my new 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
#include <iostream>
#include<string>
#include<algorithm>

using namespace std;

string erase_vowels(string sec_message)
{
	
	for (int i = 0; sec_message[i] != '\0'; i++) // erase all vowels
	{
		if (sec_message[i] == ('a') || sec_message[i] == ('e') || sec_message[i] == ('i') 
			|| sec_message[i] == ('o') || sec_message[i] == ('u'))
		{

			sec_message.erase(sec_message.begin() + i);
		}

		if (sec_message[i] == sec_message[i + 1]) //if two adjacent consonants are the same
		{                                          // erase one
			sec_message.erase(sec_message.begin()+(i+1));
		}
	}
	return sec_message;
}

string long_message(string word) // if the typed word without the vowels and without double consonants
{                              // is longer than 5, then only the first two and last three letters 
	                           //should be the output
	string filtered_word = erase_vowels(word);

	if (erase_vowels(word).length() > 5)
	{
		cout<<erase_vowels(word).substr(0,2)<< 
			erase_vowels(word).substr(erase_vowels(word).length() - 3, 3);
	}
	return filtered_word;
}


int main()
{
	char answer;
		do {
			string enter_word;
			cout << "enter a word" << endl;
			cin >> enter_word;

			string final_result=long_message(enter_word);
			cout << "the secret code is :"<<final_result << endl<<endl;
			cout << "try again(Y/N)?" << endl;
			cin >> answer;
		} while (answer == 'Y' || answer == 'y');

		return 0;
}
One more question:

when I edit line 10 to:

for (int i = 0; i <=sec_message.length(); i++)

I run the program and I get a "runtime library" window
stating :
string subscript out of range.

How come?
When i is equal to sec_message.length(), which element of sec_message do you think will be erased on line 16?
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
#include <iostream>
#include <string>

bool is_vowel( char c )
{
    static const std::string vowels = "aeiouAEIOU" ;

    // http://www.cplusplus.com/reference/string/string/find/
    return vowels.find(c) != std::string::npos ;
}

// get rid of the vowels
std::string remove_vowels( const std::string& str )
{
    std::string result ; // string with vowels removed

    // append non-vowels to the result
    // http://www.stroustrup.com/C++11FAQ.html#for
    for( char c : str ) if( !is_vowel(c) ) result += c ;

    return result ;
}

std::string remove_adjacent_duplicate( const std::string& str )
{
    if( str.empty() ) return str ;

    std::string result( 1, str.front() ) ; // initially, just the first character

    // add a subsequent character (from the substring starting at position 1)
    // only if it is not an adjacent duplicate of the last character added
    // http://www.cplusplus.com/reference/string/string/substr/
    for( char c : str.substr(1) ) if( c != result.back() ) result += c ;

    return result ;
}

// shorten to max 5 chars (first three and last two).
std::string shorten( const std::string& str )
{
    if( str.size() < 6 ) return str ;

    // http://www.cplusplus.com/reference/string/string/substr/
    else return str.substr(0,3) + str.substr( str.size()-2 ) ;
                // first three  +     last two
}

int main()
{
    const std::string str = "abebebcddef" ;

    // 1. remove vowels: bbbcddf   2. remove adjacent duplicates: bcdf   3. shorten: bcdf
    std::cout << shorten( remove_adjacent_duplicate( remove_vowels(str) ) ) << '\n' ;

    // 1. remove adjacent duplicates: abebebcdef   2. remove vowels: bbbcdf   3. shorten: bbbdf
    std::cout << shorten( remove_vowels( remove_adjacent_duplicate(str) ) ) << '\n' ;
}
@ jlborges: Thanks for your help. But is there a way to correct my code instead of rewriting the whole thing?

@ mikey boy:
I assume that the vowel elements will be erased.
So I am not sure how to correct it.
That's not answering the question I actually asked.

If you'd actually bothered to read the question I asked, and think about the answer, you might understand what's wrong with your code.
Last edited on
I read your question, that's why I answered it.
Perhaps, if my answer doesn't match your liking, instead of assuming I haven't read your question, you should perhaps
wonder about whether or not you phrased it properly.
And FYI,
the question you asked regards a modification of that line 10, that I applied
out of my own curiosity.
That has nothing to do with the whole code that is causing me problems.
Last edited on
Here's what MikeyBoy is trying to get you to see. If you change line 10 to:
for (int i = 0; i <=sec_message.length(); i++)
Then when i == sec_message.length() and you get to line 16, you'll try erasing a character past the bounds of the array. Heck, at line 15, you'll try accessing a character past the bounds of the array. So the for loop should be:

1
2
for (int i = 0; i <=sec_message.length(); i++)
for (int i = 0; i <sec_message.length(); i++)


But there's still a problem with that. At line 19, you access "the next character" (sec_message[i+1]) and when i is the maximum value (sec_message.length()-1), that access will be out of bounds.

To fix this, you should change line 19 to make sure you aren't already at the end of the string:
if (i<sec_message.length()-1 && sec_message[i] == sec_message[i + 1])
By the way, this change relies on a particular (and very handy) property of the && operator. && is guaranteed to evaluate it's left operand first. If the left operand is false, then it will not evaluate the right operand at all.

Dang, this code is getting complicated isn't it? And all that word for something that seems so simple. Worst of all, even with these changes, there's STILL a bug! Sigh. You'll see if you test with 3 or more duplicate consonants in a row.

For these reasons, I always find it easiest to solve problems like this using the basic ideas that JLBorges used: copy the good characters rather than deleting the bad ones. I can show you how to modify your code to do this if you want. There aren't many changes.
Last edited on
I read your question, that's why I answered it.

No, you really didn't. I asked a specific question about what happened when i was a specific value. I asked which specific element you thought would be erased.

You didn't answer that - instead you told me what you expected the overall functionality of your loop to be.

Read more carefully.
@dhayden, thank you very much for your explanation.
Btw, when you wrote: "the for loop should be",you didn't change
that loop at all. Is it perhaps a mistake?
When it comes to changing line 19, I can't understand why I cannot live it the way it is.
The loop I wrote, doesn't it iterate throughout the whole string and checks for duplicates?
If so, where is the need to write: if (i<sec_message.length()-1 ?

With this being said, yes,I would love you to modify my code in whichever way works, but as a beginner, I struggle in understanding other people's code, with features I haven't learned yet.
That's why I am not very comfortable with JLborge's code.

@mikeyboy: when I read
When i is equal to sec_message.length()

What I understand is, "when i is the last typed character, what happens to it?"
in fact, my answer was: if is a vowel, is erased.

What you shoulds have understood was "when i is equal to sec_message.length()" because that's what I said. Thinking about that, and realing that that is not the same thing as "when i is the last typed character", was the entire point.

And if you'd actually answered that question precisely, telling me that you believed it would be the final character, thenn that would have gotten us somewhere.

Never mind.
Btw, when you wrote: "the for loop should be",you didn't change that loop at all. Is it perhaps a mistake?

Yes. Thanks for pointing that out. I have edited my post.

When it comes to changing line 19, I can't understand why I cannot live it the way it is.
You are comparing the "current character" to the "next character". When the "current character" is the last one, what is the "next character?"

A question for you (and maybe for the prof): Do you remove double consonants in the source string, or in the result after removing vowels? For example, if the input is "abab" then there are no double consonants on the source string, but if you remove the vowels, you get "bb". So should we remove one of those b's?

By the way, this is the sort of subtle difference that good programmers have to watch out for. In the real world, we're constantly asking for clarification of the requirements for stuff like this.

Here is a modified and heavily commented version of your code. This one copies all the characters that you want to keep into the beginning positions of the string. Then it deletes whatever is leftover at the end. To do this, I keep two index variables: one is the source location where we copy from and the other is the destination location where we copy to. This version assumes that you want to skip double consonants that appear in the source string. It's pretty easy to change it to skip double consonants in the resulting string.
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
string
erase_vowels(string sec_message)
{
    // Instead of deleting the unwanted characters, I'm going to copy
    // the wanted ones into the beginning of the string.  This means
    // that sometimes I'll copy a character onto itself, but that's okay,
    // checking to avoid it is more work than just copying it.


    unsigned dst=0;    // Destination index where we copy characters TO

    // Go through each character. src is the source index where we copy
    // characters FROM
    for (unsigned src = 0; src < sec_message.length(); src++)
    {
        if (sec_message[src] == ('a') || sec_message[src] == ('e') ||
            sec_message[src] == ('i') || sec_message[src] == ('o') ||
            sec_message[src] == ('u')) {
            // Do nothing. You want to skip this character
        } else if (src < sec_message.size()-1 &&
                   sec_message[src] == sec_message[src+1]) {
            // Skip it. This is the same as the next character
        } else {
            // This one is okay. Copy it and increment the dst index
            sec_message[dst++] = sec_message[src];
        }
    }
    // If we deleted characters then there may be leftovers at the
    // end.  So remove everything from the dst index (where the next
    // character would go) to the end
    sec_message.erase(dst);
    return sec_message;
}

Topic archived. No new replies allowed.