Search

4.12 — An introduction to std::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!\n”) are placed between double quotes to identify them as strings.

Because strings are commonly used in programs, most modern programming languages include a fundamental string data type. In C++, strings aren’t a fundamental type (they’re actually a compound type, and defined in the C++ standard library rather than as part of the core language). But strings are straightforward and useful enough that we’ll introduce them here rather than wait until the chapter on compound types (chapter 8).

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 output

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

This prints:

My name is: Alex

Empty strings will print nothing:

Which prints:

[]

String input with std::cin

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 std::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. When we then used operator>> to get variable age, it extracted "Doe" instead of waiting for us to input an age. Then the program ends.

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

What the heck is std::ws?

In lesson 4.8 -- Floating point numbers, we discussed output manipulators, which allow us to alter the way output is displayed. In that lesson, we used the output manipulator function std::setprecision() to change the number of digits of precision that std::cout displayed.

C++ also supports input manipulators (defined in the iomanip header), which alter the way that input is accepted. The std::ws input manipulator tells std::cin to ignore any leading whitespace. Note that std::ws is not a function.

Let’s explore why this is useful. Consider the following program:

Here’s some output from this program:

Pick 1 or 2: 2
Now enter your name: Hello, , you picked 2

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” string, and then exits. What happened?

It turns out, when you enter a value using operator>>, std::cin not only captures the value, it also captures the newline character ('\n') that occurs when you hit the enter key. So when we type 2 and then hit enter, std::cin receives gets the string "2\n". It then extracts the 2 to variable choice, leaving the newline character behind for later. Then, when std::getline() goes to read the name, it sees "\n" is already in the stream, and figures we must have previously entered an empty string! Definitely not what was intended.

We can amend the above program to use the std::ws input manipulator, to tell std::getline() to ignore any leading whitespace characters:

Now this program will function as intended.

Pick 1 or 2: 2
Now enter your name: Alex
Hello, Alex, you picked 2

Best practice

If using std::getline to read strings, use the std::ws input manipulator to ignore leading whitespace.

Key insight

Using the extraction operator (>>) with std::cin ignores leading whitespace.
std::getline does not ignore leading whitespace unless you use input manipulator std::ws.

String length

If we want to know how many characters are in a std::string, we can ask the std::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 -- 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. 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 time

Question #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.

Show Solution


4.13 -- Literals
Index
4.11 -- Chars

608 comments to 4.12 — An introduction to std::string

  • J34NP3T3R

    int letters{ static_cast<int>(name.length()) }; // get number of letters in name (including spaces)

    why do we have to cast length() into integer ?

    • Alex

      Because std::string::length returns a size_t, which is unsigned. The static_cast tells the compiler we're okay converting an unsigned value to a signed one.

      • J34NP3T3R

        oh i see ... what happens when you dont use static ?

        int letters{ name.length() };

        will it be implicitly converted to int ?  or will it generate an error ?

  • J34NP3T3R

    i thought std::cin can only accept 1 char ?

  • kio

    Mine soultion, any notes NascarDriver or Alex?

  • Will

    Why is name.length() static_casted as int? I didn't do that in mine and it worked fine.

    • kio

      Because of the compiler settings, and how the compiler threats warnings and errors.
      https://www.learncpp.com/cpp-tutorial/configuring-your-compiler-warning-and-error-levels/

  • xixi

    hi, i cannot understand why this occur a infinite loop, i did clear the buffer.

    • nascardriver

      I suppose you didn't enter an integer, but a character that isn't convertible to an integer.
      `std::cin` enters a failed state if you do that. It won't let you do anything, not even `ignore()`, until you tell `std::cin` to leave that failed state by calling `std::cin.clear()`.

  • Niranjan

    Hi Nascar driver
    Why here

    #include <string>
    #include <iostream>

    int main()
    {
        std::cout << "Pick 1 or 2: ";
        int choice{};
        std::cin >> choice;

        std::cout << "Now enter your name: ";
        std::string name{};
        std::cin >> name;

        std::cout << "Hello, " << name << ", you picked " << choice << '\n';

        return 0;
    }
    // here it is taking both input of my name and choice
    ex:
    2
    Alex

    int main()
    {
        std::cout << "Pick 1 or 2: ";
        int choice{};
        std::cin >> choice;

        std::cout << "Now enter your name: ";
        std::string name{};
        std::getline(std::cin, name);

        std::cout << "Hello, " << name << ", you picked " << choice << '\n';

        return 0;
    }

    // here it is taking only  input of  choice and name was skipped
    ex:
    2

    //Why these two are different  when we entered "2/n"
    //In the first case when 2 was assigned to choice then "/n" should be in input buffer
    //And when std::cin >> name; is executed it should only take "/n" but why is it taking input of my name
    //since "/n" is also a string it should considered as input for std::string name;

  • konluci4

    Thank you for these tutorials they are really helpful, my solution was this:

    i think its decent but idk

  • Alex

    I wrote the program of the question in the same exact way as your solution (in exception of the variable "age" that I directly initialized as a type double) but I struggled to run it.
    When I wrote:

    int letters{ static_cast<int>(name.length()) };

    the program won't compile, but if I copy and paste that line from the solution the program runs correctly.
    Do you have any idea why this happens?

    Another question is about the lenght() member function:
    The retrun type of this function is a char?

    • nascardriver

      Can you post your full code and exact error message?

      `length()` returns some unsigned integer type.

      • Alex

        Here are the errors (some parts are written in Italian though.)

        Errore (attivo)    E0135    class "std::basic_string<char, std::char_traits<char>, std::allocator<char>>" non include alcun membro "lenght"      (Translation: does not include any member "lenght")

        Errore    C2039    'lenght': non è un membro di 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'  (Translation: 'lenght': is not a member of )

  • yeokaiwei

    In the "Mixing std::cin and std::getline()" example, you mention a newline. However, where is the "\n"? I can only see it in the last line.

    • nascardriver

      The line feed is entered by the user when they press enter.

      • yeokaiwei

        So, pressing Enter turns your keyboard input into "\n"?

      • J34NP3T3R

        hi nascardriver sorry for asking again but i am confused with this

        int main()
        {
            std::cout << "Enter your name: ";

            std::string yourName;
            std::getline(std::cin, yourName);                                      // i assume the input is now stored in variable yourName
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');    // ignore unlimited characters until a \n is removed

            // but cin still awaits input and the cursor is waiting.

            std::cout << "Your name is : " << yourName;

        }

        why is this ? when i enter my name "PETER" then press ENTER the cursor waits for input again and i have to press ENTER again before it moves to cout which displays my name.

        • nascardriver

          `std::getline` removes the line feed, leaving the input empty. `std::cin.ignore` then asks for input again, because there is none.

  • LearningCpp

    Was it also valid to not use the static_cast<int> and instead just do:

    As .length() does return a size_t and size_t being a typedef for unsigned int, as seen in lesson 6.15 for the priority of operands, if i understand correctly the unsigned int will be promoted to double and it should work(?). At least it seems to work in my code.

    • nascardriver

      If you don't want to store the length in an `int`, you don't need to cast it. The `std::size_t` returned by `name.length()` will be converted to a `double` if you use it directly.

      `std::size_t` is _not_ (always) an `unsigned int`. It's some unsigned integer type.

  • Zephirus

    Just looking at the solution - why not use functions?

    I find it easier to read and understand. It also means we don't need to static_cast the age to divide - just return a double! Just wondering why.

  • Imagine

    In the solution to the question why was

    static_cast used? is name.length() not already an int?

  • Brian

    Hi. Thanks for the great work on these tutorials, I'm finding them really helpful.

    I have a query about the quiz question here:
    When I call std::cin.ignore after a successful std::getline, I'm being prompted for another input again - presumably because the cin buffer is empty. I'm wondering if there's a way to only call std::cin.ignore if the buffer is not empty or whether there's something else I've done wrong?
    Here's my getName() function

  • Martin

    Suggestion: I think the explanation
    "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."
    is very helpful for all those who don't know member functions. However, member functions are used earlier already, e.g.

    on this page and

    in chapter O, so maybe the explanation should come earlier, too.

  • TavonCpp

    was creating agePerLetter really necessary?

  • Tony

    Hey @nascardriver, how about this solution?

    At first I haven't thought about data-type priority. But since "double" has higher priority, the final result (double/string) will be a double. Are there any issues with this code? Thanks again :)

  • It's+Me

    Pretty neat and confusing Quiz :D

  • Math

    why did we have to make it int? Isn't the length already a number?

    • It's+Me

      I questioned that myself too!
      any answer?

      • nascardriver

        `length()` returns an unsigned integer. Because unsigned integers don't always do what we want, a cast to `int` is used.

        • yagoo

          If i do something like this:

          static_cast<double>(myAge) / myName.length()

          The other operand is already a double, so even if myName.length() is unsigned integer, it should be automatically converted to double which is higher priority right? Is this safe or length() still should be explicitly converted to int first?

          • nascardriver

            If you cast `myAge` to `double`, everything is fine, because `myName.length()` get converted to `double`.
            If you were cast `myAge` to `int`, you need a cast on `myName.length()` too, because otherwise `myAge` would be promoted to `unsigned int`.

  • Math

    Hello, I've heard that <string> header file was added to the <iostream> header file so I was wondering why you guys still include it, is it best practice or is it fine if I don't include it?

    • nascardriver

      iostream does not include string. Even if it did, it would be bad practice to rely on transitive includes. Include everything you use.

  • Pionoplayer

    Reposted because for some reason trying to use the code tags while editing the comment breaks the code block

    Hey I've run into a strange problem, specifically that visual studio doesn't seem to support concatenation via the + symbol.

    This throws a compile error, telling me that "expression must have an integral or unscoped enum type". Am I doing something wrong or do I need to screw around with settings somewhere in visual studio?

    EDIT: worth noting,

    works just fine for some reason.

    • nascardriver

      Code tags work after refreshing the page if you edit a comment. But code tags are broken right now anyway.
      `std::string` supports `+`, but "thing" is not a `std::string`, it's a string literal and string literals can't do anything. If you want to print one thing after the other, use `<<`.

      Using string concatenating for this is very wasteful. If you need to concatenate two string, use `std::string`.

      • Pionoplayer

        Ah, I get it. I don't remember exactly what I was doing that led to me discovering this, but I'll keep that in mind, thank you!

  • Luxary

    So, if std::cin captures whitespaces, which are then filtered and sent back to the input stream, then why did our previous examples using integers work fine?
    Say you have something pretty basic like this:

    We enter 2 for x and 3 for y, (which really are 2\n and 3\n), and it works just fine.
    Wouldn't the second std::cin have to deal with "\n3\n", with the first '\n' being the filtered newline from the first input?

    Does std::cin ignore leading whitespace while std::getline() doesn't? (Hence why cleaning with std::cin.ignore() is required).
    If that's the case, I feel like it should be emphasized to better explain why what we did so far worked fine, and why we should be careful from now on, knowing std::cin a little better.

  • Zolee

    Hi, I have written the code without static_cast, could you please point out why this is not a good way to do it?

    • nascardriver

      The solution used `int`, because humans usually don't say "I'm 10.3 years old". Humans round ages to the next lower integer: "I'm 10 years old.". If you make `age` a `double`, you don't need the cast.

  • tiara

    > operator>> only returns characters up to the first whitespace it encounters.

    does this operator ignores whitespace at the first of an input line? or just ignored them at the end?

    • nascardriver

      It ignores whitespace before the value. The trailing whitespace is left in the input buffer and either extracted or ignore the next time you try to read from it.

  • ashly

    Hey,

    >We picked that number because it’s the largest signed value guaranteed to fit in a (2-byte) integer on all platforms.
    Does this mean the size of a buffer is 2-byte signed integer? if so, we put a sequential of characters into a buffer, why its size considered as signed 2-byte integer value?

    • nascardriver

      There's no good reason behind this choice. The size of the buffer is unknown. If you want to do it properly, use `std::numeric_limits::max()`.

  • sami

    Isn't it better to say "into a string variable"?

    "To read a full line of input into a string..."

  • AE35_Unit

    Okay, got this one.  I must have missed something in the previous chapters, as I can't get getline() to work with int's, so I went to std::cin and added a line to .ignore(32767, '\n'); in case the age function is called again int the future.  Also, would there ever  be a time to use, std::cin.ignore(32767, "\n"); (note double-quotes "\n"),  Thanks as always.

    • nascardriver

      `std::getline` only works with strings, see https://en.cppreference.com/w/cpp/string/basic_string/getline
      `std::cin.ignore` wants an `int` (Oddly enough) as it's second argument, not a stirng.

      What's up with those "f_" prefixes?

      • AE35_Unit

        Thanks for clarification on getline, that's is what I thought but just wanted to be sure. With regard to std::ignore(32767, '\n'), you said it wants an 'int', is the 32767 the 'int' you are referring too?

        the "f_" prefix helps me to distinguish what variable I have chosen to work inside a function.  I could use age inside the function as well as outside ie. in main(), but using f_ inside a function helps me to remember which variables I'm using inside and outside of the functions. Is there a generally accepted standard for this?  I wouldn't mind switching over to that. Thanks again for your help.  cheers.

        • nascardriver

          > is the 32767 the 'int' you are referring too
          The '\n' is the int (Remember, `char` is an integral type).

          > the "f_" prefix
          I don't understand it, because you're not using the prefix in `main` and `calcYears`. Naming and formatting is up to the developer, as long as it's done consistently. There is no general best practice. Global variables are often prefixed with "g_". Variable names can be reused across different functions, in case that's what you're worried about.

  • Mike

    Hi nascardriver!

    I've found a few inconsistencies. First, the headers are not sorted alphabetically in any of the examples above. I also think it would be informative to add a line explaining that std::getline() is declared inside the "string" header.
    Also, you haven't zero-initialized variables "name" and "age" in the second example inside "string input and output" and also variable "name" in the last example inside "Mixing std::cin and std::getline()".

    I was also wondering what the value is when we're zero-initializing the string variable?

  • Denis

    In quiz answer: why do we need to explicitly cast name.length() to an int on line 14, since string.length() already returns an int?

    • nascardriver

      `string.length()` returns some unsigned integer type. Converting an unsigned integer to a signed integer is a narrowing conversion. Narrowing conversions aren't allowed in list initialization, because they can cause loss of data.

  • Gabe

    Quiz

Leave a Comment

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