Can't make getline work in C++ strings

I really don't know what happened but getline started to not working. Making it cin instead of getline works.

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

using namespace std;

int main()
{
    cout << "What do you want to make?" << endl;
    cout << "1) Encryption" << endl;
    cout << "2) Decryption" << endl; 
    cout << "\nEnter your choice: ";


    string true_chars  {"1234567890"};
    string false_chars {"3905416728"};
    string entered_chars {};

    char choice {};
    cin >> choice;

    if (choice == '1')
    {
        cout << "Enter the number that you wanting to encrypt: ";        
        getline(cin, entered_chars);  

        for (int i {}; i < entered_chars.length(); i++)
        {   
            entered_chars.at(i) = false_chars.at(true_chars.find(entered_chars.at(i)));
        }

        cout << "Here is you encrypted number: ";
        for (auto characters: entered_chars)
            cout << characters;

        entered_chars.clear();
        cout << endl;
    }

    else if (choice == '2')
    { 
        cout << "Enter the number that you wanting to decrypt: ";
        getline(cin, entered_chars);
        
        for (int i {}; i < entered_chars.length(); i++)
        {   
            entered_chars.at(i) = true_chars.at(false_chars.find(entered_chars.at(i)));
        }

        cout << "Here is your decrypted number: ";
        for (auto characters: entered_chars)
            cout << characters;

        entered_chars.clear();
        cout << endl;
    }

    else
    {
        cout << "You entered wrong choice, you need to enter '1' or '2' ";
    }

    return 0;
}
Last edited on
Mixing >> and getline easily leads to problems. There are at least three different approaches to deal with this (pick one).

1. Read the input line by line by always using getline (never >>). Use std::istringstream to extract the data that you need from each line.
1
2
3
4
5
std::string line;
std::getline(std::cin, line);
std::istringstream iss(line);
int x;
iss >> x; // you read from the a string stream the same way as with cin 


2. In every place in the code where you use >> (once or multiple times) you always make sure to read the whole line so that it will not interfere with other input code that comes later.
1
2
3
int x;
std::cin >> x;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // discards the rest of the line 


3. Use >> as normal but before using getline you always use ws to discard any leading whitespace (newlines, spaces, tabs, etc.).
1
2
std::string line;
std::getline(std::cin >> std::ws, line);
Note that this will not allow you to enter empty lines and any space at the beginning of the line will be discarded.

I think #3 is easiest if you never want the user to be able to enter empty line and never care about spaces at the beginning of lines, otherwise I think #1 is a pretty robust solution but a bit more work.
Last edited on
So you're saying I shouldn't use extractor and getline simultaneously in code. Even if they're separated from each other. In the first one as you showed I can't use getline with char inputs. I can convert a char variable into a string but it can hold more storage than char, doesn't it?

I'm not understanding your second one. I'm not that advanced I guess. As well as the third one.

The interesting part is the instructors didn't mention it to me yet. It's good to learn. Thanks for your help :)

Edit: In the first one you forgot the mention about <sstream>
Last edited on
So you're saying I shouldn't use extractor and getline simultaneously in code. Even if they're separated from each other.

Let me explain what the problem is...

The input from the user is just a sequence of characters. This is also known as a stream (std::cin and objects of type std::istringstream are "input streams").

When the user press enter to feed a line of input to the program there will be a newline character ('\n') added at the end.

When you read from the stream it will read character by character, continuing from where it left off earlier.

When you use getline it will read all characters until it finds a newline character.

When you use >> it will first ignore any whitespace character and then it will read the value. Anything that comes after the value (including the newline character at the end of the line) is still left in the stream for other operations to read.

Now, let's consider the following code:
1
2
3
4
5
6
7
8
char ch;
std::cin >> ch;

std::string line;
std::getline(std::cin, line);

std::cout << "char: '" << ch << "'\n";
std::cout << "line: '" << line << "'\n";


If you write A and press enter the stream will contain "A\n".
std::cin >> ch; reads the A and leaves newline character in the stream "\n".
Then std::getline(std::cin, line); will run and start looking for a newline character. It finds one right away so it gives you an empty string (without waiting for more input).

If you instead write Hello and press enter the stream will contain "Hello\n".
std::cin >> ch; reads the H and the stream is left with the content "ello\n".
Then std::getline(std::cin, line); reads all the characters "ello" up until the newline character and store them in the string.

Sometimes this is what you want. You might want to read whatever is left on the line after reading something at the beginning of the line with >>. But usually (as is the case in your program) it's not what you want and that's why you need to work around this somehow.


In the first one as you showed I can't use getline with char inputs. I can convert a char variable into a string but it can hold more storage than char, doesn't it?

Yes. That's why I said you can use a std::istringstream to extract a char, or whatever data type you want, from the line string.


I'm not understanding your second one.

The ignore function is used to discard characters from the stream. It will stop when it has discarded as many characters as specified by the first argument, or as soon as it finds the character that was passed as the second argument, whichever comes first.

In this case I wanted to discard the rest of the line. That's why I passed the newline character as the second argument.
And I don't care how long the line is so that is why I passed the *maximum possible value as the first argument.

* The first argument should be of type std::streamsize (or a type that is implicitly convertible to std::streamsize).
std::numeric_limits<std::streamsize>::max() is the maximum value that the type std::streamsize can store.

See
https://www.cplusplus.com/reference/istream/istream/ignore/
https://www.cplusplus.com/reference/limits/numeric_limits/
https://www.cplusplus.com/reference/ios/streamsize/

That said, just passing some large integer is probably also fine.
 
std::cin.ignore(1000, '\n');
Who writes lines that are longer than 1000 character, right?

As well as the third one.

The line
 
std::getline(std::cin >> std::ws, line);
has the same meaning as
1
2
std::cin >> std::ws;
std::getline(std::cin, line);


std::cin >> std::ws discards all leading whitespace characters. If the stream only contained a newline character before then it will discard the newline character and wait for more input (and continue to discard all whitespace characters) until the user enters a non-whitespace character.


Edit: In the first one you forgot the mention about <sstream>

Some of the things that I mention here requires additional headers. You'll have to look into that yourself. ;)

It's time for me to sleep now so I won't be able to respond to any follow up questions in the next few hours, just so that you know... 😴
Last edited on
Thanks again for help and your big effort. This taught me I have to work further even I learn (watch and practice) the subject. And yes you told what's streaming with ease. Have a good day.
Consider:

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

bool check_num(const std::string& str) {
    for (const auto& ch : str)
        if (!std::isdigit(static_cast<unsigned char>(ch)))
            return false;

    return true;
}

int main() {
    const std::string true_chars {"1234567890"};
    const std::string false_chars {"3905416728"};

    for (char choice {}; choice != '3'; ) {
        std::string entered_chars;

        std::cout << "\nWhat do you want to make?\n";
        std::cout << "1) Encryption\n";
        std::cout << "2) Decryption\n";
        std::cout << "3) Quit\n";
        std::cout << "\nEnter your choice: ";

        std::cin >> choice;

        if (std::cin.peek() != '\n') {
            std::cout << "Invalid option\n";
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            choice = '0';
            continue;
        }

        std::cin.ignore();

        switch (choice) {
            case '1':
                std::cout << "Enter the number that you wanting to encrypt: ";
                std::getline(std::cin, entered_chars);

                if (check_num(entered_chars)) {
                    for (auto& ch : entered_chars)
                        ch = false_chars.at(true_chars.find(ch));

                    std::cout << "Here is you encrypted number: " << entered_chars << '\n';
                } else
                    std::cout << "Invalid number\n";

                break;

            case '2':
                std::cout << "Enter the number that you wanting to decrypt: ";
                std::getline(std::cin, entered_chars);

                if (check_num(entered_chars)) {
                    for (auto& ch : entered_chars)
                        ch = true_chars.at(false_chars.find(ch));

                    std::cout << "Here is your decrypted number: " << entered_chars << '\n';
                } else
                    std::cout << "Invalid number\n";

                break;

            case '3':
                break;

            default:
                std::cout << "Invalid option\n";
                break;
        }
    }
}

Hey, thanks for your help. It's looking promising. But it looks more complex than other code. At least I see it like that. I'm such a beginner, maybe that's why. Have a good day.
Topic archived. No new replies allowed.