Search

4.4b — An introduction to std::string

What is a string?

The very first C++ program you wrote probably looked something like this:

So what is “Hello, world!” exactly? “Hello, world!” is a collection of sequential characters called a string. In C++, we use strings to represent text such as names, addresses, words, and sentences. String literals (such as “Hello, world!”) are placed between double quotes to identify them as a string.

Because strings are commonly used in programs, most modern languages include a built-in string data type. C++ includes one, not as part of the core language, but as part of the standard library.

std::string

To use strings in C++, we first need to #include the <string> header to bring in the declarations for std::string. Once that is done, we can define variables of type std::string.

Just like normal variables, you can initialize or assign values to strings as you would expect:

Note that strings can hold numbers as well:

In string form, numbers are treated as text, not numbers, and thus they can not be manipulated as numbers (e.g. you can’t multiply them). C++ will not automatically convert string numbers to integer or floating point values.

String input and output

Strings can be output as expected using std::cout:

This prints:

My name is: Alex

However, using strings with std::cin may yield some surprises! Consider the following example:

Here’s the results from a sample run of this program:

Enter your full name: John Doe
Enter your age: Your name is John and your age is Doe

Hmmm, that isn’t right! What happened? It turns out that when using operator>> to extract a string from cin, operator>> only returns characters up to the first whitespace it encounters. Any other characters are left inside cin, waiting for the next extraction.

So when we used operator>> to extract a string into variable name, only “John” was extracted, leaving “Doe” inside std::cin, waiting for the next extraction. When we used operator>> again to extract a string into variable age, we got “Doe” instead of “23”. If we had done a third extraction, we would have gotten “23”.

Use std::getline() to input text

To read a full line of input into a string, you’re better off using the std::getline() function instead. std::getline() takes two parameters: the first is std::cin, and the second is your string variable.

Here’s the same program as above using std::getline():

Now our program works as expected:

Enter your full name: John Doe
Enter your age: 23
Your name is John Doe and your age is 23

Mixing std::cin and std::getline()

Reading inputs with both std::cin and std::getline may cause some unexpected behavior. Consider the following:

This program first asks you to enter 1 or 2, and waits for you to do so. All good so far. Then it will ask you to enter your name. However, it won’t actually wait for you to enter your name! Instead, it prints the “Hello” line, and then exits. What happened?

It turns out, when you enter a numeric value using cin, cin not only captures the numeric value, it also captures the newline. So when we enter 2, cin actually gets the string “2\n”. It then extracts the 2 to variable choice, leaving the newline stuck in the input stream. Then, when std::getline goes to read the name, it sees “\n” is already in the stream, and figures we must have entered an empty string! Definitely not what was intended.

A good rule of thumb is that after reading a numeric value with std::cin, remove the newline from the stream. This can be done using the following:

If we insert this line directly after reading variable choice, the extraneous newline will be removed from the stream, and the program will work as expected!

Rule: If reading numeric values with std::cin, it’s a good idea to remove the extraneous newline using std::cin.ignore().

What’s that 32767 magic number in your code?

That tells std::cin.ignore() how many characters to ignore up to. We picked that number because it’s the largest signed value guaranteed to fit in a (2-byte) integer on all platforms.

Technically, the correct way to ignore an unlimited amount of input is as follows:

But this requires remembering (or looking up) that horrendous line of code, as well as remembering what header to include. Most of the time you won’t need to ignore more than a line or two of buffered input, so for practical purposes, 32767 works about as well, and has the benefit of being something you can actually remember in your head.

Throughout these tutorials, we use 32767 for this reason. However, it’s your choice of whether you want to do it the “obscure, complex, and correct” way or the “easy, practical, but not ideal” way.

Appending strings

You can use operator+ to concatenate two strings together, or operator+= to append one string to another.

Here’s an example of both, also showing what happens if you try to use operator+ to add two numeric strings together:

This prints:

4511
45 volts

Note that operator+ concatenated the strings “45” and “11” into “4511”. It did not add them as numbers.

String length

If we want to know how long a string is, we can ask the string for its length. The syntax for doing this is different than you’ve seen before, but is pretty straightforward:

This prints:

Alex has 4 characters

Note that instead of asking for the string length as length(myName), we say myName.length().

The length function isn’t a normal standalone function like we’ve used up to this point -- it’s a special type of function that belongs to std::string called a member function. We’ll cover member functions, including how to write your own, in more detail later.

Conclusion

std::string is complex, leveraging many language features that we haven’t covered yet. It also has a lot of other capabilities that we haven’t touched on here. Fortunately, you don’t need to understand these complexities to use std::string for simple tasks, like basic string input and output. We encourage you to start experimenting with strings now, and we’ll cover additional string capabilities later.

Quiz

1) Write a program that asks the user to enter their full name and their age. As output, tell the user how many years they’ve lived for each letter in their name (for simplicity, count spaces as a letter).

Sample output:

Enter your full name: John Doe
Enter your age: 46
You've lived 5.75 years for each letter in your name.

Quiz solutions

1) Show Solution

4.5 -- Enumerated types
Index
4.4a -- Explicit type conversion (casting)

300 comments to 4.4b — An introduction to std::string

  • why this code is true when I use std::cin.igore()

    • Hi btnal!

      @std::cin.operator>> leaves a '\n' in the input stream. @std::getline reads only until the next '\n', which is the first character in the stream. Calling @std::cin.ignore without arguments clears one character from the input stream, leaving it empty. Now @std::getline will wait until the user input new text.

  • Kio

    Hi Alex,
    At the very start of the "lesson", you have leftover of std::endl;

  • Matt

    I'm confused.
    when I copy the solution code into M$ Visual Studio 2017 I get errors related to std::getline.

    namespace "std" has no member "getline"
    'getline': is not a member of 'std'
    'getline': identifier not found

    What did I miss?
    Thanks

  • Sean W

    These lessons are extremely helpful, thanks so much!
    There is something I find confusing in the example of combining std::cin and std::getline().
    Why is the residual \n in the cin stream not a problem in other contexts? If I change line 12 of the code, such that the string variable 'name' gets input from std::cin and not from std::getline, I don't have the problem of residual \n left in the stream from the previous numerical input.
    So it seems the residual \n in the cin stream is only read when subsequently using std::getline but not std::cin. Why would that be?

    • Hi Sean!

      That's just how these functions are defined.
      @std::basic_istream::operator>>(int &) skips leading whitespace, then tries to extract an int.
      @std::getline(std::istream &, std::string &) extracts all input until '\n' is found.

      For more information, have a look at the documentation:
      std::getline - http://www.cplusplus.com/reference/string/string/getline/
      std::basic_istream::operator>> - http://www.cplusplus.com/reference/istream/basic_istream/operator%3E%3E/
      std::noskipws - http://www.cplusplus.com/reference/ios/noskipws/
      std::isspace - http://www.cplusplus.com/reference/cctype/isspace/

      • Sean W

        Thanks for the quick reply. That was helpful.
        Another thing that confused me was the nature of the fix (std::cin.ignore(32767, '\n')). I get that you need to tell the getline function to ignore the residual \n and read line of text that follows, but why the need to ignore characters before the \n when there aren't any characters there to ignore? I guess this is just a good practice because there may be other situations where there are characters before the \n in the input stream that do need to be ignored?

        • >

          That's bad, use

          > there may be other situations where there are characters before the \n in the input stream that do need to be ignored
          Yes, eg.

  • Aditi

    My Solution:

    • * Line 8, 10, 12, 14: Initialize your variables with uniform initialization
      * Line 10: Inconsistent variable names
      * Line 10: Missing cast to int

  • Hi Alex,

    One of the easiest programs to write so far (or am I just getting better at this as I go along?).

    My program is thus:

    Short and sweet and (hopefully) does just as the specification asked.

    • > am I just getting better at this as I go along?
      Hopefully, that's what you're here for.

      > Short and sweet and (hopefully) does just as the specification asked.
      Yep, suggestions:
      * Line 11, 12, 20, 21: Initialize your variables with uniform initialization
      * Line 20: @std::string::length returns an @std::size_t, you should cast it. And since you have to cast anyway, you can directly cast it to a double rather than an int.

  • Sid

    Hi there!
    So I have two doubts:
    1)I have seen some people using strlen() instead of the .length function that you provided. What is the difference between these two?

    2)Why does std::string work even though I don't include<string>?

    Thanks in Advance!

  • firebow

    why do i need to use doubles?

  • Arumikaze

    Hello again! I have successfully completed the question without a problem, but I had some difficulty running the program when I compiled it. For some reason Avira anti virus kept detecting something whenever I tried to run the program. The error message was "HEUR /APC (Cloud)". I managed to get around it by restoring the file and setting it so that it ignores it. I didn't have this issue with previous programs, so maybe it has to do something with strings? I highly doubt it and it might be something else on my end, but just wanted to let you know. Thanks!

    I'll post my code anyway.

    • Hi Arumikaze!

      * Line 9, 17, 18: Initialize your variables with uniform initialization
      * Line 15: Use @std::numeric_limits<std::streamsize>::max() instead of 32767
      * Line 17: @std::string::length returns a @size_t, you need a cast or change the type of @nameLength to @size_t

      References:
      @std::string::length - http://www.cplusplus.com/reference/string/string/length/
      @std::istream::ignore - http://www.cplusplus.com/reference/istream/istream/ignore/

  • Nguyen

    Hi Alex & nascardriver,

    "...when you enter a numeric value using cin, cin not only captures the numeric value, it also captures the newline. So when we enter 2, cin actually gets the string “2\n”. It then extracts the 2 to variable choice, leaving the newline stuck in the input stream. Then, when std::getline goes to read the name, it sees “\n” is already in the stream, and figures we must have entered an empty string!"

    Could you please explain if I change line 11 & 12 to followings?

    int anotherChoice;
    cin >> anotherChoice;

    Does cin see "\n" that is already in the stream?
    Please explain the differences in both.

    Thanks, Have a great day.

    • Alex

      Yes, but cin will discard leading whitespace when reading non-char/non-string data. So in this case, the '\n' is discarded, and then the stream is empty, so it waits for input.

  • Nucklead

    I found a funny thing about std::cout. In the following code one may think that console prints "You entered: " first and only then "Enter a number: " but, in fact, it makes the opposite. Of course, I intended this behavior but didn't think it will work. So, I guess std::cout first evaluates everything it has to print and only than actually prints it, am I right?

    Also, another question. When I didn't #include <string> yet, typing std::string didn't show any error about missing type. Why?

    • Hi Nucklead!

      > So, I guess std::cout first evaluates everything it has to print and only than actually prints it, am I right?
      The order is undefined. If you want a specific order, call your functions first and store the results in temporaries before printing them.

      > When I didn't #include <string> yet, typing std::string didn't show any error about missing type. Why?
      Includes are transitive, iostream includes string.

      • Nucklead

        Got it. I was just playing around with std::string, but in the end I found out that using function directly instead of temporary variable is generaly a bad idea, so yes, I will stick to temporaries.

  • Hello Alex, I want edit the exercise 2 by input name of animal in function "printNumberofleg" but I see mistake. Please help show me the error.
    Also, I'm new member, so I don't know how to post the code same another people (there numbers follow code row in this here).
    Thank you very much!

    /.................................................../
    #include <iostream>
    #include <string>

    enum Animal
    {
        pig,
        chicken,
        goat,
        cat,
        dog,
        ostrich
    };

    std::string getanimalname (Animal name)
    {
        switch (name)
        {
            case pig:
                return std::string ("pig");
            case chicken:
                return std::string ("chicken");
            case goat:
                return std::string ("goat");
            case cat:
                return std::string ("cat");
            case dog:
                return std::string ("dog");
            case ostrich:
                return std::string ("ostrich");
            default:
                return std::string ("error case");
        }
        
    }
    void printNumberofleg (Animal name)
    {
        std::cout << "A " << getanimalname (name) << " has ";
        switch (name)
        {
            case chicken:
            case ostrich:
                std::cout << "2 ";
                break;
            
            case pig:
            case goat:
            case cat:
            case dog:
                std::cout << "4 ";
                break;
            default:
                std::cout << "error leg" << std::endl;
                break;
        
        }
        std::cout << "legs." << std::endl;
    }

    int main()
    {
        std::cout << "Enter animal name: ";
        std::string name;
        std::getline(std::cin, name);
        
        printNumberofleg (name);
        
        
        system ("pause");
        return 0;
    }

    • nascardriver

      Hi Minh!

      @printNumberofleg expects an @Animal as parameter, you're passing it an @std::string.

      [/CODE]

  • Marijana

    Hello, one question: Why do you recommend not using "using namespace std"? I recognized you had suggested it to a lot of people in the comments
    Greets

  • ayush

    not getting the output

    • nascardriver

  • Jack

    We are told to always initialize our variables, yet in:

    and

    and

    We are leaving variables (name, age, choice) uninitialized. Any reason why?

    • nascardriver

      Hi Jack!

      > Any reason why?
      Uninitialized variables aren't a problem for std::cin after C++11, but this assumption shouldn't be made. I guess Alex wrote this lesson before introducing the always-initialize-rule or he just didn't initialize them because he's used to not initializing variables when he assigns a value later.
      Anyway, you shouldn't do the same, initialize everything.

      • Alex

        A little bit of both. 🙂

        I've updated the example to initialize the values.

        Note I don't initialize std::string. std::string is a special type (a class) that will self-initialize, so providing an explicit initialization value isn't necessary. We cover classes in chapter 8.

  • Quamruz Zaman

    Hi Alex / nascardriver,
       I have been using below mentioned line of code to ignore the whitespaces instead of cin.ignore function. Can you comment, Is it good idea to use it or not ?

    • nascardriver

      Hi Quamruz!

      That's a bad idea. Instead of calling one function, your calling two. You need an extra variable. @std::istream needs to deal with extraction rather than just discarding the input. Use

      • Quamruz Zaman

        Thanks for the Quick Response nascardriver.

        Two things didn't get very clear to me:
        1. Instead of calling one function you are calling two.
             To skip whitespaces we just need to use cin>>ws in place of cin.ignore(). So, isn't it just one call.
        2. You need an extra variable.
             - Can you be a little specific about this (which variable).

        And one more thing for the case of input:
        _____ Hello World!!  // consider underscore as spaces

        cin.ignore method will have a string which will have spaces prefixed as it is in input. So, isn't it bad? I mean we should skip all the whitespaces instead of just newline character.

        • nascardriver

          There was a mix up.
          @std::istream::ignore is _not_ used to remove whitespace. It removes everything until it has either discarded n characters (which is infinite with @std::numeric_limits<std::streamsize>::max()) or until it finds the delimiter ('\n').
          Your code removes leading whitespace followed by a string extraction into @identifier.

          • Quamruz Zaman

            So, i think it is better to use cin>>ws whenever we have mixed reads of datatypes as for the cin.ignore case it even can reject some useful input character(s) until it finds the one it is looking for ( '\n' in our case ). Can i say so ?

            • nascardriver

              They are different things with different purposes. @std::istream::ignore is usually used once you've detected that the input stream is in a bad state and needs to be cleared. @std::ws is used, well, when you want to remove whitespace.

  • Henry

    Hi Alex

    Is there a particular reason for preferring to use iostream over stdio?
    What are the advantages of cout and cin over printf and scanf?

    Thanks in advance
    Mr. Grimough

    • nascardriver

      Hi Henry!

      The io operators can be overloaded to define input/output functions for custom types whereas @std::printf requires you to call these functions manually.
      @std::printf has some formatting options that are easier to use there than in @std::ostream.

      References
      * Lesson 9.3 - Overloading the I/O operators

  • Jon

    Okay, haven't posted a test since the gravity ball one back in 2.10! But use of strings is something I really want to get solid!

    Here's my test:

    Learn CPP 4.4b.cpp

    I'm not sure the best way to get it not to error if I input a string for the age, however. std::stoi() doesn't really like getting letters it turns out.

    • nascardriver

      Hi Jon!

      * Line 6, 22, 24: Initialize your variables with uniform initialization.
      * Reading the age as an @std::string and converting it to an int is slow. Read the age as an int.

      Other than that your code looks fine, good job!

      • Jon

        Hey nascardriver!

        Yeah, I'm caught in a pickle about the initialization! My work, for whom I'm learning C++, seems to prefer the '=' form of initialization so I've been getting in that habit, even if it's not as good as uniform.

        As for reading age as int, would you suggest another more specific function to read an int - of is there some way that I could keep my function as generic, but do a check on the input to ensure it won't have to convert? Or would that check be just as slow as converting it?

        • nascardriver

          You could use templates, but for now I'd stick to writing a separate function or use std::cin::operator>> directly without a wrapper.

          References
          * Chapter 13 - Templates

  • RelearningCGuy

    Is there a reason you declare strings with direct initialization instead of uniform initialization?

    Why not:

    • nascardriver

      Hi there!

      Some of the lessons are older than uniform initialization (Uniform initialization was add to C++ in 2011).
      Uniform initialization is preferred.

      • RelearningCGuy

        Ah gotcha, thanks. I've been testing everything as I go along, and it seems like the new uniform initialization works basically everywhere (even in for loops, etc).  Are there any cases in 11+ in which it is beneficial to use direct instead?

        • nascardriver

          Uniform initialization can cause unexpected behavior in combination with the 'auto' keyword.

          References
          * Lesson 4.8 - The auto keyword

          • RelearningCGuy

            Makes sense since it would not know to what type it should be initialized, especially if you tried to init to default values {}. Thanks!

Leave a Comment

Put all code inside code tags: [code]your code here[/code]