Урок №57. Введение в std::string

  Юрий  | 

    | 

  Обновл. 20 Апр 2019  | 

 47225

 ǀ   39 

Вашей первой программой на языке C++, вероятно, было:

Не так ли? Но что такое Hello, world!? Hello, world! — это последовательность символов или просто строка (англ. «string»). В C++ мы используем строки для представления текста (имён, адресов, слов и предложений). Строковые литералы (такие как Hello, world!) помещаются в двойные кавычки.

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

Тип данных string

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

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

Строки также могут содержать числа:

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

Ввод/вывод строк


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

Результат выполнения программы выше:

My name is Sasha

А вот с std::cin дела обстоят несколько иначе. Рассмотрим следующий пример:

Результат выполнения программы выше:

Enter your full name: Sasha Mak
Enter your age: Your name is Sasha and your age is Mak

Хм, что-то не так! Что же случилось? Оказывается, оператор извлечения (>>) возвращает символы из входного потока данных только до первого пробела. Все остальные символы остаются внутри cin, ожидая следующего извлечения.

Поэтому, когда мы использовали оператор >> для извлечения данных в переменную myName, только Sasha был извлечён, Mak остался внутри std::cin, ожидая следующего извлечения. Когда мы использовали оператор >> снова, чтобы извлечь данные в переменную myAge, мы получили Mak вместо 25. Если бы мы сделали третье извлечение, то получили бы 25.

Использование std::getline()

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

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

Теперь программа работает как надо:

Enter your full name: Sasha Mak
Enter your age: 25
Your name is Sasha Mak and your age is 25

Использование std::getline() c std::cin


Извлечение данных с std::cin с помощью std::getline() иногда может приводить к неожиданным результатам. Например, рассмотрим следующую программу:

Возможно, вы удивитесь, но когда вы запустите эту программу, и она попросит вас ввести ваше имя, она не будет ожидать вашего ввода, а сразу выведет результат (просто пробел вместо вашего имени)! Пробный запуск программы выше:

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

Почему так? Оказывается, когда вы вводите числовое значение, поток cin захватывает вместе с вашим числом и символ новой строки. Так что, когда мы ввели 2, cin фактически получил 2\n. Затем он извлёк значение 2 в переменную, оставляя \n (символ новой строки) во входном потоке. Затем, когда std::getline() извлекает данные для myName, он видит в потоке \n и думает, что мы, должно быть, ввели просто пустую строку! А это определённо не то, что мы хотим.

Хорошей практикой считается удалять из входного потока данных символ новой строки. Это можно сделать следующим образом:

Если мы вставим эту строчку непосредственно после получения входных данных, то символ новой строки будет удалён из входного потока, и программа будет работать должным образом:

Правило: При вводе числовых значений не забывайте удалять символ новой строки из входного потока данных с помощью std::cin.ignore().

Добавление строк

Вы можете использовать оператор + для объединения двух строк или оператор += для добавления одной строки к другой.

В следующей программе мы протестируем эти два оператора, а также покажем, что произойдёт, если вы попытаетесь использовать оператор + для добавления двух числовых строк:

Результат выполнения программы выше:

4412
44 cats

Обратите внимание, оператор + объединил две числовые строки в одну (44 + 12 = 4412). Он не добавлял эти строки как числа.

Длина строк


Чтобы узнать длину строки, мы можем сделать следующее:

Результат выполнения программы выше:

Sasha has 5 characters

Обратите внимание, вместо запроса длины строки как length(myName), мы пишем myName.length().

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

Тест

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

Enter your full name: Tom Cats
Enter your age: 45
You've lived 5.625 years for each letter in your name.

Уточнение: Возраст 45 делится на Tom Cats (8 букв, учитывая пробел), что равно 5.625.

Ответ

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (136 оценок, среднее: 4,86 из 5)
Загрузка...

Комментариев: 39

  1. Аватар havvk01:

  2. Аватар Илья:

    Здравствуйте!
    У меня три вопроса:
    1) Почему Вы в своём ответе на тестовый вопрос не используете стейтмент cin.ignore (32767, '\n'); ?
    2) Что это вообще за магическое число такое 32767? откуда оно взялось? Почему не использовать меньшее, например 100 (врядли в мире есть имя и фамилия длиной больше ста символов. Ну если есть, то использовать 200, 300, 500 в конце концов=) ну не 32 тысячи же=) )
    3) Почему у меня программа после получения имени и фамилии после первого нажатия Enter просто переходит на следующую строку в консоли и только после повторного нажатия Enter выводит запрос возраста. Заранее большое спасибо за помощь!
    Вот программа:

    1. Аватар Константин:

      32767 — это (как я догадываюсь) число символов, которые можно разместить в одной строке; в конце компилятор сам (втихаря) вставляет \n

    2. Аватар Cerberus:

      "Почему у меня программа после получения имени и фамилии после первого нажатия Enter просто переходит на следующую строку в консоли и только после повторного нажатия Enter выводит запрос возраста"

      std::getline забирает строку целиком, вместе с символом перевода строки. А после этого Вы говорите, что cin должен проигнорировать следующий перевод строки, прежде чем что-то делать дальше. Вот он и ждал, пока этот самый перевод строки появится в потоке ввода — то есть, пока Вы не нажмёте Enter.

  3. Аватар Денис:

    По поводу getline разъяснил Георгий в комментариях (3 августа 2018), спасибо
    Чуть-чуть в ступоре был, почему string русский выводил в виде символов, ведь setlocal был прописан, оказалось всё просто:) Ещё скрыл символы после точки double при выводе результата, может кому-то будет полезно (если float писать вместо "%.2lf", нужно "%.2f", где 2 (кол-во символов после точки), но результат, как я понял, округляется. Будьте осторожней при необходимости вывести точный результат (или проверьте мои слова, я не уверен). И да, я решил без функций, с ними дольше, а программа маленькая.

  4. Аватар Вячеслав:

    у меня так получилось:

  5. Аватар Игорь:

    У меня как-то размазано получилось по сравнению с ответом )))

  6. Аватар Alexey:

    Подскажите, насколько код написан красиво, вроде старался следовать всем рекомендациям.

  7. Аватар Алексей:

  8. Аватар somebox:

    Что-то у меня не получается. Ввожу имя "Саша", возраст — "12" и получаю "1,5" на выходе, а должен же 4. Что не так? Вот код:

    1. Аватар Владимир:

      Скопировал программу, у меня всё нормально выдаёт, но выдать он должен 3, а не 4 (12 / 4 = 3)

  9. Аватар Giveyn:

    А почему сразу myAge не присвоить double, и обойтись без промежуточного agePerLetter, а деление выполнить в строке вывода?

  10. Аватар Ярослав:

  11. Аватар Katerina:

    Здравствуйте, подскажите, пожалуйста, почему в приведенной функции выскакивает предупреждение? Я так понимаю linker не доволен… но программа компилируется и работает правильно вроде. Заранее благодарна.

    1. Аватар Георгий:

      Функция "std::getline" получает от пользователя не цифру, а строку, так что число которое ты введёшь, будет просто строкой, а не числом (звучит странно), при этом ты пытаешься в тип данных Int "впихнуть" строчку. Проще говоря, это тоже самое, что написать:

      Надеюсь, я объяснил понятно 🙂

      1. Аватар Георгий:

        А нет, я пересмотрел программу, и не знаю в чём проблема 😀

    2. Аватар Torgu:

      было бы неплохо увидеть само предупреждение

      1. Аватар Katerina:

        Добрый вечер! Спасибо за feedback.
        Ниже код и предупреждение:

        Файл io.h

        Файл io.cpp

        Файл main.cpp

        Предупреждение в файле io.cpp в строке int nameLength = name.length();

        Implicit conversion loses integer precision: 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::s

        Названия файлов просто лень переименовывать )))
        Спасибо!

        1. Аватар Get Leevz:

          Метод length() возвращает тип unsigned int, который вы присваиваете переменной signed int. Из-за этого происходит неявное преобразование с возможностью потери точности. Это можно решить с помощью изначального типа переменной unsigned int, но я бы просто забил, ведь чтобы произошло переполнение, пользователь должен ввести имя длинною более чем в 2 147 483 647 символов, а это…хммм, как бы немного многовато

        2. Аватар Katerina:

          Get Leevz, почему-то не могу ответить на ваш комментарий, поэтому отвечаю на свой )) Спасибо вам большое за разъяснения, сама бы до этого не дошла!

  12. Аватар Sergey Groysman:

    Юрий, доброго дня. Подскажите, в чём разница в данных записях, если работают обе, переводя число в double и мы получаем на выходе число с плавающей точкой.

    вместо

    можно ли обходиться в принципе без static_cast?
    Спасибо.

    1. Юрий Юрий:

      Привет. Ответ на ваш вопрос находится в уроке 56.

      1. Аватар Torgu:

        А обязательно переводить вручную? Я просто дал переменной возраста тип float. Компилятор же сам преобразовывает типы

  13. Аватар Серж:

    Перечитал урок 7.
    Проблема 1
    Как использовать русский язык в консоли C++?
    Слово в слово повторено как в программе выше и где сказано что всё работает.
    У меня в CodeBlocks не работает. Возможно в студии будет работать , не проверял.
    Остается в уроке 7 прочитать совет в самом низу: Во-первых, спросите Google…

    1. Юрий Юрий:

      Верно, если Google не поможет, спросите на StackOverflow. Код рабочий, если у вас не работает, значит у вас уже специфическая проблема: возможно кодировка в Code:Blocks у вас не та, что нужно. Возможно, у вас что-то подключено (плагин, расширение или что-либо другое), что блокирует корректное выполнение setlocale.

      1. Аватар Серж:

        Ясно, спасибо. Подключена русификация интерфейса и все. Пока не искал решения проблемы, т.к. это не сильно влияет на изучение.
        Огромное спасибо за такой нужный труд по переводу и поддержанию сайта с этим учебным курсом. Из всего что я находил ,это самое лучшее. Спасибо!!!

        1. Юрий Юрий:

          Пожалуйста 🙂

          Попробуйте отключить русификацию интерфейса и посмотрите какая у вас кодировка стоит — сбросьте всё до значений по умолчанию. И если уже никак, то вы знаете, где искать.

  14. Аватар Максим:

  15. Аватар Ka4:

    Скажите, пожалуйста, что я не так сделал!

    1. Юрий Юрий:

      Ваш код рабочий. Результаты совпадают.

      Что не так:

      1. У вас функция im9_famili9 выполняет сразу два задания: получает строку и высчитывает её длину. (Функция должна выполнять одно задание)

      2. Функции result нужно было присвоить тип void и ничего не возвращать, а сразу выводить результат в консоль. А то у вас result выводит результат и возвращает его еще в main() — зачем и почему? В main вы с переменной z ничего не делаете, её объявление вообще не нужно.

      Всё остальное гуд.

      1. Аватар Ka4:

        Точно..
        Спасибо больше!)
        Отличные уроки!!!
        Догоняю программу в ВУЗе с помощью этого сайта!

        1. Юрий Юрий:

          Если честно, то я тоже свою программу из универа догнал и перегнал именно по этим урокам 🙂

  16. Аватар Герман:

    Спасибо!!!

    1. Юрий Юрий:

      Пожалуйста 🙂

  17. Аватар Герман:

    Уважаемый автор, почему при выводе в консоль значение переменной myName, не поддерживается кириллица!

    1. Юрий Юрий:

      Нужно подключить Windows.h (заголовочный файл) и прописать строчки:

      Вот всё работает:

      1. Аватар Серж:

        Почему-то в CodeBlocks 17.12 выводит этим способом кракозябры как на ввод ,так и на вывод. Если использовать setlocale(LC_ALL, "rus"); или setlocale(LC_ALL, ""); вывод идет на русском , а на ввод кракозябры ,если использовать
        SetConsoleCP(1251);
        SetConsoleOutputCP(1251);
        то даже при подключенном setlocale(LC_ALL, "rus"); все равно кракозябры.

      2. Аватар GOGOLEK:

        Разве setlocale(LC_ALL, "rus") уже не помогает?

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *