4.12 – Знакомство с std::string

Добавлено 2 мая 2021 в 10:23

Самая первая написанная вами программа на C++, вероятно, выглядела примерно так:

#include <iostream>
 
int main()
{
    std::cout << "Hello, world!\n";
    return 0;
}

Так что точно такое "Hello, world!"? "Hello, world!" представляет собой набор последовательных символов, называемый строкой. В C++ мы используем строки для представления текста, такого как имена, адреса, слова и предложения. Строковые литералы (например, "Hello, world!\n") помещаются в двойные кавычки, чтобы идентифицировать их как строки.

Поскольку в программах строки обычно используются, большинство современных языков программирования включают в себя базовый строковый тип данных. В C++ строки не являются базовым типом (на самом деле они являются составным типом и определены в стандартной библиотеке C++, а не как часть ядра языка). Но строки достаточно просты и полезны, поэтому мы представим их здесь, а не будем дожидаться главы о составных типах (глава 8).

std::string

Чтобы использовать строки в C++, нам сначала нужно включить через #include заголовочный файл <string>, чтобы ввести объявления для std::string. Как только это будет сделано, мы сможем определить переменные типа std::string.

#include <string>      // разрешает использование std::string
 
std::string myName {}; // пустая строка

Вы можете инициализировать или присваивать значения строкам, как и обычным переменным, что и следовало ожидать:

std::string myName{ "Alex" }; // инициализируем myName строковым литералом "Alex"
myName = "John";              // присваиваем переменной myName строковый литерал "John"

Обратите внимание, что строки также могут содержать числа:

std::string myID{ "45" }; // "45" не то же самое, что целое число 45!

В строковой форме числа обрабатываются как текст, а не как числа, и поэтому ими нельзя манипулировать как числами (например, вы не можете их умножать). C++ не будет автоматически преобразовывать строковые числа в целочисленные значения или значения с плавающей запятой.

Вывод строк

Строки можно выводить, как и ожидалось, с помощью std::cout:

#include <iostream>
#include <string>
 
int main()
{
    std::string myName{ "Alex" };
    std::cout << "My name is: " << myName << '\n';
 
    return 0;
}

Эта программа печатает:

My name is: Alex

Пустые строки ничего не напечатают:

#include <iostream>
#include <string>
 
int main()
{
    std::string empty{ };
    std::cout << '[' << empty << ']';
 
    return 0;
}

Эта программа напечатает:

[]

Ввод строк с помощью std::cin

Использование строк с std::cin может преподнести сюрпризы! Рассмотрим следующий пример:

#include <iostream>
#include <string>
 
int main()
{
    std::cout << "Enter your full name: ";
    std::string name{};
    std::cin >> name; // это не будет работать должным образом, 
                      // так как std::cin прерывается на пробелах
 
    std::cout << "Enter your age: ";
    std::string age{};
    std::cin >> age;
 
    std::cout << "Your name is " << name << " and your age is " << age << '\n';
 
    return 0;
}

Ниже показаны результаты пробного запуска этой программы:

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

Хммм, это неправильно! Что случилось? Оказывается, что при использовании operator>> для извлечения из cin строки operator>> возвращает только символы до первого попавшегося пробела. Все остальные символы остаются внутри std::cin в ожидании следующего извлечения.

Поэтому, когда мы использовали operator>> для извлечения строки в переменную name, было извлечено только "John", оставив "Doe" внутри std::cin. Когда мы снова использовали operator>> для получения переменной age, он извлек "Doe" вместо того, чтобы ждать, пока мы введем возраст. На этом программа заканчивается.

Для ввода текста используйте std::getline()

Чтобы прочитать всю строку входных данных в переменную строки, лучше вместо этого использовать функцию std::getline(). std::getline() принимает два параметра: первый – это std::cin, а второй – ваша строковая переменная.

Вот та же программа, что и выше, с использованием std::getline():

#include <string>     // для std::string и std::getline
#include <iostream>
#include <iomanip>    // для std::ws
 
int main()
{
    std::cout << "Enter your full name: ";
    std::string name{};
    std::getline(std::cin >> std::ws, name); // считываем полную строку текста в name
 
    std::cout << "Enter your age: ";
    std::string age{};
    std::getline(std::cin >> std::ws, age); // считываем полную строку текста в age
 
    std::cout << "Your name is " << name << " and your age is " << age << '\n';
 
    return 0;
}

Теперь наша программа работает как и ожидалось:

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

Что еще за std::ws?

В уроке «4.8 – Числовые типы с плавающей точкой» мы обсудили манипуляторы вывода, которые позволяют нам изменять способ отображения выводимых значений. В том уроке мы использовали функцию манипулятора вывода std::setprecision(), чтобы изменить количество значащих цифр, отображаемых std::cout.

C++ также поддерживает манипуляторы ввода (определенные в заголовке iomanip), которые изменяют способ приема входных данных. Манипулятор ввода std::ws говорит std::cin игнорировать любые начальные пробелы. Обратите внимание, что std::ws не является функцией.

Давайте разберемся, чем он полезен. Рассмотрим следующую программу:

#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::getline(std::cin, name); // примечание: здесь нет std::ws
 
    std::cout << "Hello, " << name << ", you picked " << choice << '\n';
 
    return 0;
}

Вот пример результата работы этой программы:

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

Данная программа сначала просит вас ввести 1 или 2 и ждет, когда вы это сделаете. Пока всё хорошо. Затем она просит вас ввести свое имя. Однако на самом деле она не будет ждать, пока вы введете свое имя! Вместо этого она печатает строку "Hello", а затем завершит работу. Что случилось?

Оказывается, когда вы вводите значение с помощью operator>>, std::cin не только захватывает значение, но также захватывает символ новой строки ('\n'), который появляется, когда вы нажимаете клавишу Enter. Итак, когда мы набираем 2 и нажимаем Enter, std::cin получает строку "2\n". Затем он извлекает 2 в переменную choice, оставляя символ новой строки на потом. Затем, когда std::getline() переходит к чтению имени, он видит, что "\n" уже находится в потоке, и полагает, что мы, должно быть, ввели пустую строку! Это определенно не то, что было задумано.

Мы можем изменить приведенную выше программу, чтобы использовать манипулятор ввода std::ws, чтобы указать std::getline() игнорировать любые начальные пробельные символы:

#include <string>
#include <iostream>
#include <iomanip> // для std::ws
 
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 >> std::ws, name); // примечание: здесь добавлен std::ws
 
    std::cout << "Hello, " << name << ", you picked " << choice << '\n';
 
    return 0;
}

Теперь эта программа будет работать так, как задумано.

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

Лучшая практика


Если для чтения строк используется функция std::getline, используйте манипулятор ввода std::ws, чтобы игнорировать начальные пробелы.

Ключевые выводы


Использование оператора извлечения (>>) с std::cin игнорирует начальные пробелы.

std::getline не игнорирует начальные пробелы, если вы не используете манипулятор ввода std::ws.

Длина строки

Если мы хотим знать, сколько символов находится в std::string, мы можем спросить переменную std::string о ее длине. Синтаксис для этого отличается от того, что вы видели раньше, но он довольно прост:

#include <iostream>
#include <string>
 
int main()
{
    std::string myName{ "Alex" };
    std::cout << myName << " has " << myName.length() << " characters\n";
    return 0;
}

Это напечатает:

Alex has 4 characters

Обратите внимание, что вместо того, чтобы запрашивать длину строки как length(myName), мы говорим myName.length(). Функция length() не является обычной автономной функцией – это особый тип функции, которая принадлежит std::string и называется функцией-членом. Позже мы расскажем о функциях-членах, в том числе о том, как написать свои собственные.

Заключение

std::string сложен и использует многие языковые функции, которые мы еще не рассмотрели. К счастью, вам не нужно разбираться в этих сложностях, чтобы использовать std::string для простых задач, таких как простейший ввод и вывод строк. Мы рекомендуем вам начать экспериментировать со строками прямо сейчас, а дополнительные возможности строк мы рассмотрим позже.

Небольшой тест

Вопрос 1

Напишите программу, которая просит пользователя ввести свое полное имя и возраст. В качестве вывода сообщите пользователю, сколько лет он прожил для каждой буквы в своем имени (для простоты считайте пробелы как букву).

Пример вывода:

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

#include <iostream>
#include <string>
#include <iomanip>
 
int main()
{
    std::cout << "Enter your full name: ";
    std::string name{};
    std::getline(std::cin >> std::ws, name); // читаем полную текстовую строку в name
 
    std::cout << "Enter your age: ";

    // возраст должен быть целым числом, а не строкой, чтобы мы могли 
    // выполнять с ним математические вычисления
    int age{};
    std::cin >> age;
 
    // получаем количество букв в имени (включая пробелы)
    int letters{ static_cast<int>(name.length()) }; 
    // статическое приведение age к double, чтобы избежать целочисленного деления
    double agePerLetter{ static_cast<double>(age) / letters };
    std::cout << "You've lived " << agePerLetter << " years for each letter in your name.\n";
 
    return 0;
}

Теги

C++ / Cppiomanipstd::cinstd::stringstd::wsSTL / Standard Template Library / Стандартная библиотека шаблоновДля начинающихМанипулятор вводаОбучениеПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.