Нормальное извлечение целочисленного значения будет успешным, если поток сможет прочитать любое целочисленное значение.То есть, если есть хотя бы одна цифра, необязательно, за которой следует что-либо, чтение целого числа завершается успешно.Обычные операции извлечения не пытаются читать больше, в частности, они не пытаются найти следующий пробел.
Судя по звукам, вы хотите быть уверены, чтоэто пробел после вашего номера и сбой, если его нет.Я могу думать о двух разных подходах, чтобы сделать это:
- Создать простой манипулятор, который проверяет, находится ли поток на пробеле.Это, однако, означает, что вы будете читать ваши значения, используя что-то вроде
in >> value >> is_space
. - Создайте пользовательский
std::num_get<char>
фасет, установите его в std::locale
и imbue()
this std::locale
в свойпоток (ов).Это немного сложнее, но не требует каких-либо изменений в способе чтения целых чисел.
Создание такого манипулятора довольно тривиально:
std::istream& is_space(std::istream& in)
{
if (!std::isspace(in.peek()))
{
in.setstate(std::ios_base::failbit);
}
return in;
}
Теперь изменимспособ чтения чисел более интересен, и я подозреваю, что только что назвал ряд стандартных библиотечных классов, о которых большинство людей даже не подозревают.Итак, давайте быстро напишем пример для этого.Я изменю фасет std::num_get<char>
только для работы с unsigned int
: чтобы сделать это для других целочисленных типов, необходимо переопределить больше функций.Итак, вот замена для std::num_get<char>
фасета:
class num_get:
public std::num_get<char>
{
iter_type do_get(iter_type it, iter_type end,
std::ios_base& ios, std::ios_base::iostate& err,
unsigned int& value) const
{
it = std::num_get<char>::do_get(it, end, ios, err, value);
if (it != end && !isspace(static_cast<unsigned char>(*it)))
{
err |= std::ios_base::failbit;
}
return it;
}
};
Все, что нужно сделать, это извлечь класс из std::num_get<char>
и переопределить одну из его виртуальных функций.Реализация этой функции довольно проста: начните с чтения значения путем делегирования базовому классу (я только что понял, что виртуальные функции действительно хотят защищать, а не закрывать, как я это делал в прошлом, но это совершенно другое обсуждение),Независимо от того, было ли это успешно (если это не так, он установит состояние ошибки в err), переопределение проверяет, есть ли другой доступный символ, и, если так, проверяет, является ли это пробелом, и если нет, устанавливает std::ios_base::failbit
в результате ошибки err
.
Осталось настроить поток на использование этого конкретного фасета в std::locale
и подключить новый std::locale
к потоку:
std::locale loc(std::locale(), new num_get);
in.imbue(loc);
std::locale
s и его фасеты подсчитываются внутренними ссылками, т. Е. Вы не должны отслеживать указатель на фасет, и вам не нужно также хранить std::locale
.Если вам кажется неудобным imbue()
созданный std::locale
или вы хотите использовать эту измененную логику везде, вы можете установить глобальный std::locale
, который используется для инициализации любого вновь созданного потока, для использования пользовательского std::num_get<char>
фаска.