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

ΦXocę 웃 Пepeúpa ツ
  • 43,054
  • 16
  • 58
  • 83
liaforu
  • 3
  • 1
  • 2
    @FantasticMrFox That's not entirely true. In practice they are and even in theory `someString[someString.length()]` is garantueed to return `'\0'`. – churill Feb 25 '21 at 06:34
  • Does this answer your question? [Why does std::getline() skip input after a formatted extraction?](https://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-input-after-a-formatted-extraction) – churill Feb 25 '21 at 06: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! :) – kkm Feb 25 '21 at 06: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. – kkm Feb 25 '21 at 06:54

3 Answers3

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');
Casey
  • 8,686
  • 9
  • 52
  • 79
  • good catch! Is there a way to autoflush the stream? – Antonin GAVREL Feb 25 '21 at 06:38
  • 1
    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 Feb 25 '21 at 06:40
  • @churill You are correct. I have it backwards. It's past midnight here and I mis-read the Notes section. Fixed. :) – Casey Feb 25 '21 at 06:46
  • @AntoninGAVREL Not that I'm aware. – Casey Feb 25 '21 at 06:50
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;
}
Meowmere
  • 3,924
  • 1
  • 14
  • 33
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> 
arfneto
  • 604
  • 1
  • 3
  • 11