Литмир - Электронная Библиотека
A
A

идти налево, пока не увидишь картину справа на стене

сними картину со стены и открой дверь позади нее. Возьми сундук

В данном случае мы сначала прочитаем всю строку, а затем извлечем из нее отдельные слова.

string command;

getline(cin,command);             // вводим строку

stringstream ss(command);

vector<string> words;

string s;

while (ss>>s) words.push_back(s); // извлекаем отдельные слова

С другой стороны, если есть выбор, то лучше всего ориентироваться на знаки пунктуации, а не на символ перехода на новую строку. 

11.6. Классификация символов

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

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

1+4*x<=y/z*5
на одиннадцать лексем.

1 + 4 * x <= y / z * 5

Для ввода чисел мы могли бы использовать оператор

>>
, но, пытаясь ввести идентификаторы как строки, должны были бы прочитать фразу
x<=y
как целую строку (поскольку символы
<
и
=
не являются разделителями). Сочетание символов
z*
мы также должны были бы ввести как целую строку (поскольку символ
*
также не является разделителем).

Вместо этого можно сделать следующее:

char ch;

while (cin.get(ch)) {

  if (isspace(ch)) { // если символ ch является разделителем,

                     // ничего не делаем (так как разделители

                     // игнорируются)

  }

  if (isdigit(ch)) {

                     // вводим число

  }

  else if (isalpha(ch)) {

                     // вводим идентификатор

  }

  else {

                     // обрабатываем операторы

  }

}

Функция

istream::get()
считывает отдельный символ в свой аргумент. Разделители при этом не игнорируются. Как и оператор
>>
, функция
get()
возвращает ссылку на свой поток
istream
, так что можно проверить его состояние.

При вводе отдельных символов мы обычно хотим классифицировать их: это символ или цифра? В верхнем регистре или в нижнем? И так далее. Для этого существует набор стандартных библиотечных функций.

Программирование. Принципы и практика использования C++ Исправленное издание - _094.png

 Обратите внимание на то, что категории классификации можно объединять с помощью оператора ИЛИ (

||
). Например, выражение
isalnum(c)
означает
isalpha(c)||isdigit(c);
иначе говоря, “является ли символ c буквой или цифрой?”

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

Программирование. Принципы и практика использования C++ Исправленное издание - _095.png

Это удобно, когда мы хотим устранить различия между символами, набранными в разных регистрах. Например, если пользователь ввел слова

Right
,
right
и
rigHT
, то, скорее всего, он имел в виду одно и то же (например, слово
rigHT
чаще всего является результатом нечаянного нажатия клавиши <Caps Lock>). Применив функцию
tolower()
к каждому символу в каждой из строк, мы можем получить одно и то же значение:
right
. Эту операцию можно выполнить с любым объектом класса
string
.

void tolower(string&amp; s) // вводит строку s в нижнем регистре

{

  for (int i=0; i&lt;s.length(); ++i) s[i] = tolower(s[i]);

}

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Для того чтобы действительно изменить объект класса
string
, используем передачу аргумента по ссылке (см. раздел 8.5.5). Если бы мы хотели сохранить старую строку без изменения, то могли бы написать функцию, создающую ее копию в нижнем регистре. Мы предпочитаем функцию
tolower()
, а не
toupper()
, поскольку она лучше работает с текстами на некоторых естественных языках, например немецком, в которых не каждый символ в нижнем регистре имеет эквивалент в верхнем регистре. 

11.7. Использование нестандартных разделителей

В этом разделе мы рассмотрим гипотетические примеры использования потоков i

ostream
для решения реальных задач. При вводе строк слова по умолчанию разделяются пробелами или другими специальными символами (whitespace). К сожалению, поток
istream
не имеет средств, позволяющих определять, какие символы должны играть роль разделителей, или непосредственно изменять способ, с помощью которого оператор
&gt;&gt;
считывает строки. Итак, что делать, если мы хотим дать другое определение разделителю? Рассмотрим пример из раздела 4.6.3, в котором мы считывали слова и сравнивали их друг с другом. Между этими словами стояли разделители, поэтому если мы вводили строку

As planned, the guests arrived; then

то получали слова

As

planned,

161
{"b":"847443","o":1}