идти налево, пока не увидишь картину справа на стене
сними картину со стены и открой дверь позади нее. Возьми сундук
В данном случае мы сначала прочитаем всю строку, а затем извлечем из нее отдельные слова.
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
, так что можно проверить его состояние.
При вводе отдельных символов мы обычно хотим классифицировать их: это символ или цифра? В верхнем регистре или в нижнем? И так далее. Для этого существует набор стандартных библиотечных функций.
Обратите внимание на то, что категории классификации можно объединять с помощью оператора ИЛИ (
||
). Например, выражение
isalnum(c)
означает
isalpha(c)||isdigit(c);
иначе говоря, “является ли символ c буквой или цифрой?”
Кроме того, в стандартной библиотеке есть две полезные функции для уничтожения различий между символами, набранными в разных регистрах.
Это удобно, когда мы хотим устранить различия между символами, набранными в разных регистрах. Например, если пользователь ввел слова
Right
,
right
и
rigHT
, то, скорее всего, он имел в виду одно и то же (например, слово
rigHT
чаще всего является результатом нечаянного нажатия клавиши <Caps Lock>). Применив функцию
tolower()
к каждому символу в каждой из строк, мы можем получить одно и то же значение:
right
. Эту операцию можно выполнить с любым объектом класса
string
.
void tolower(string& s) // вводит строку s в нижнем регистре
{
for (int i=0; i<s.length(); ++i) s[i] = tolower(s[i]);
}
Для того чтобы действительно изменить объект класса
string
, используем передачу аргумента по ссылке (см. раздел 8.5.5). Если бы мы хотели сохранить старую строку без изменения, то могли бы написать функцию, создающую ее копию в нижнем регистре. Мы предпочитаем функцию
tolower()
, а не
toupper()
, поскольку она лучше работает с текстами на некоторых естественных языках, например немецком, в которых не каждый символ в нижнем регистре имеет эквивалент в верхнем регистре.
11.7. Использование нестандартных разделителей
В этом разделе мы рассмотрим гипотетические примеры использования потоков i
ostream
для решения реальных задач. При вводе строк слова по умолчанию разделяются пробелами или другими специальными символами (whitespace). К сожалению, поток
istream
не имеет средств, позволяющих определять, какие символы должны играть роль разделителей, или непосредственно изменять способ, с помощью которого оператор
>>
считывает строки. Итак, что делать, если мы хотим дать другое определение разделителю? Рассмотрим пример из раздела 4.6.3, в котором мы считывали слова и сравнивали их друг с другом. Между этими словами стояли разделители, поэтому если мы вводили строку
As planned, the guests arrived; then
то получали слова
As
planned,