0

This program is supposed to encrypt and decrypt the string "myWord". Once received, it passes through the two methods to encrypt and decrypt and then returns to main. I have been trying to make it prompt the user if they want to continue or not. If they enter "n" then the loops stops and the program ends. if they enter "y" then it will repeat and they will be able to encrypt again after they are prompted for their name again. I've tried many different ways but the output continues to end up like this after I enter y:

#include <iostream> #include <string> using namespace std; void encrypt(string); void decrypt(string); int main() { string myWord; char choice = 'y'; while (choice == 'y') { cout << "Enter a name: "; getline(cin, myWord); encrypt(myWord); cout << "Would you like to encrypt again? (y/n): "; cin >> choice; if(choice == 'n') { return 0; } system("pause"); } return 0; } void encrypt(string encrypting) { for (int i = 0; encrypting[i] != '\0'; i++) { encrypting[i] = encrypting[i] + 1; } cout <<"Encrypted: " << encrypting << endl; decrypt(encrypting); } void decrypt(string decrypting) { for (int i = 0; decrypting[i] != '\0'; i++) { decrypting[i] = decrypting[i] - 1; } cout << "Decrypted: " << decrypting << endl; return; } 

My output

1

4
  • 2
    @FantasticMrFox That's not entirely true. In practice they are and even in theory someString[someString.length()] is garantueed to return '\0'. Commented Feb 25, 2021 at 6:34
  • Does this answer your question? Why does std::getline() skip input after a formatted extraction? Commented Feb 25, 2021 at 6:37
  • Hi liaforu, and welcome to SO! You did it right by posting a working code example, but the best questions contain a minimal reproduction, with stuff that's only relevant to the question. Your question is related to user input; encryption and decryption is irrelevant. You could simplify your example by replacing the line calling encrypt() with std::cout << "doing encryption\n"; and removing the other two functions. Think of this: it wouldn't change the essence of your question, still reproduce the issue, but trim the example in half and make it much quicker to read! :) Commented Feb 25, 2021 at 6:41
  • @FantasticMrFox, this is not true. N4713, the latest working draft of C++17 (identical to the standard but available for free) quoth, in §5.13.5.16: “After any necessary concatenation, in translation phase 7 [...], ’\0’ is appended to every string literal so that programs that scan a string can find its end.” This is in the normative text, outside of square brackets. If your compiler does it differently, it's a bug in the compiler. Commented Feb 25, 2021 at 6:54

3 Answers 3

2

Your issue is switching between whitespace-delimited input and line-oriented input. Since std::cin leaves the '\n' in the stream, the next call to std::getline sees that and skips ahead too.

You need to skip to the end of the stream:

See https://en.cppreference.com/w/cpp/string/basic_string/getline for more information.

This should fix your problem:

#include <limits> #include <ios> //... cout << "Enter a name: "; getline(cin, myWord); encrypt(myWord); cout << "Would you like to encrypt again? (y/n): "; cin >> choice; cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
Sign up to request clarification or add additional context in comments.

4 Comments

good catch! Is there a way to autoflush the stream?
No. In the reference you linked see bullet point 1.2.b) "the next available input character is delim, as tested by Traits::eq(c, delim), in which case the delimiter character is extracted from input, but is not appended to str." - the endline _is_extracted. Actually the issue is the opposite, the cin >> choice; leaves a \n in the input, thus the next getline reads an empty string.
@churill You are correct. I have it backwards. It's past midnight here and I mis-read the Notes section. Fixed. :)
@AntoninGAVREL Not that I'm aware.
0

Mixing formatted input (e.g. std::cin >> x) and unformatted input (e.g. std::getline) is hard to get right.

In your example, the cin >> choice line would extract a character from stream (ignoring leading whitespace). The next character in the stream (assume you typed y then enter) would be the newline character \n.

For example, your input sequence might look like this name\ny\nanother_name.

The first getline call extracts name\n, giving you the string name and discarding \n (it is still extracted, just not added to the string), leaving y\nanother_name.

Then cin >> choice extracts y, leaving \nanother_name.

Then you call getline again, it looks at the beginning of a stream, see a \n, and thinks it gets an empty line. So it extracts \n and discards it... You wanted it to read another_name, but it reads an empty line.

The simplest possible solution is to do a cin >> std::ws before you call std::getline. This extracts all the leading whitespace from the input stream and discards them (that includes the spurious empty line)

In your example, it will be:

cout << "Would you like to encrypt again? (y/n): "; cin >> choice; cin >> std::ws; // Note the added std::ws here if(choice == 'n') { return 0; } 

Comments

0

Well, as people here told you it is not safe to mix char, strings, getline() and cin stuff...

You may find it simpler to just go on C++ fashion and use only getline() and string an in this

Example

#include <iostream> #include <string> using namespace std; void decrypt(string); void encrypt(string); int main(void) { string choice; do { string myWord; // only exists here cout << "Enter a name: "; getline( cin, myWord ); encrypt(myWord); cout << "Would you like to encrypt again? (y/n): "; getline( cin, choice ); } while (choice == "y"); return 0; } void encrypt(string encrypting) { for (int i = 0; encrypting[i] != '\0'; i++) { encrypting[i] = encrypting[i] + 1; } cout <<"Encrypted: " << encrypting << endl; decrypt(encrypting); } void decrypt(string decrypting) { for (int i = 0; decrypting[i] != '\0'; i++) { decrypting[i] = decrypting[i] - 1; } cout << "Decrypted: " << decrypting << endl; return; } 

That shows

PS C:\test> g++ -o x -Wall x.cpp PS C:\test> ./x Enter a name: A test Encrypted: B!uftu Decrypted: A test Would you like to encrypt again? (y/n): y Enter a name: Another test Encrypted: Bopuifs!uftu Decrypted: Another test Would you like to encrypt again? (y/n): y Enter a name: Last One Encrypted: Mbtu!Pof Decrypted: Last One Would you like to encrypt again? (y/n): n PS C:\test> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.