Cin sync c что это
Русские Блоги
cin.clear () и связанное с ним использование
1. cin.clear()
Используется для изменения индикатора состояния cin.
cin.sync () используется для очистки потока данных в области кэша.
Если идентификатор не изменился, его нельзя ввести, даже если поток данных очищен. Таким образом, эти два должны использоваться вместе.
Мы определяем переменную, которая будет введена как целое число, но если мы введем английские буквы или китайские символы, произойдет ошибка. В cin есть метод для обнаружения этой ошибки, который называется cin.rdstate (); когда cin.rdstate () При возврате 0 (то есть ios :: goodbit) ошибки нет, и вы можете продолжить ввод или работу. Если вы вернете 4, произойдет нефатальная ошибка, то есть ios :: failbit, вы не сможете продолжать вводить или работать. И cin.clear может контролировать нас. Идентификация этой проблемы в cin. Язык следующий: cin.clear (идентификатор); идентификационный символ:
Goodbit без ошибок
Eofbit достиг конца файла
нефитальная ошибка ввода / вывода при сбое, исправимая
badbit Фатальная ошибка ввода / вывода, не может быть исправлена. Если она находится в классе ввода / вывода, вам необходимо добавить ios :: identifier
С помощью cin.clear мы можем подтвердить его внутренний идентификатор и повторно ввести его, если он введен неправильно. В сочетании с методом реального очищения потока данных cin.sync () см. следующий пример:
2. cin.ignore()
Метод cin.ignore (a, ch) предназначен для извлечения символов из входного потока (cin). Извлеченные символы игнорируются и не используются. Каждый раз, когда символ отбрасывается, он подсчитывает и сравнивает символы: если число достигает a или символ, который отбрасывается, является ch, выполнение функции cin.ignore () прекращается, в противном случае он продолжает ждать. Обычной функцией является очистка содержимого буфера ввода, заканчивающегося возвратом каретки, что исключает влияние предыдущего ввода на следующий ввод. Например, это может быть использовано: cin.ignore (1024, ‘\ n’), обычно первый параметр устанавливается достаточно большим, так что на самом деле всегда работает только второй параметр ‘\ n’, так что это предложение ставится Все символы перед возвратом каретки (включая возврат каретки) удаляются из входного буфера (потока).
Если вы введете bcdabcd в адрес, то в это время в потоке останется bcd \ n, а cin.ignore (), в это время съедено b, что оставит оставшийся cd \ n в потоке непосредственно в cin.getline ( str3,30); должен быть символ \ n, поэтому getline возвращается прямо сюда.
3 Сравнение cin.sync () и cin.ignor ()
Но поскольку программа не всегда знает ход выполнения внешнего ввода во время выполнения программы, трудно контролировать, очищено ли содержимое буфера ввода. Часто мы можем просто отказаться от части, а не от всего входного буфера. Например, очистка текущей строки или очистка символов новой строки в конце строки. Но если в буфере уже есть следующая строка, эта часть может быть тем, что мы хотим сохранить. В настоящее время лучше не использовать sync (). Попробуйте вместо этого использовать функцию игнорирования.
cin.ignore (numeric_limits :: max (), ’/ n’); // очистить текущую строку
cin.ignore (numeric_limits :: max ()); // Очистить все в cin
Использование ignore, очевидно, более точно контролирует буфер, чем sync ().
Насколько медленны iostreams?
Потоки ввода-вывода в стандартной библиотеке C++ просты в использовании, типобезопасны, устойчивы к утечке ресурсов, и позволяют простую обработку ошибок. Однако, за ними закрепилась репутация «медленных». Этому есть несколько причин, таких как широкое использование динамической аллокации и виртуальных функций. Вообще, потоки — одна из самых древних частей стандартной библиотеки (они начали использоваться примерно в 1988 году), и многие решения в них сейчас воспринимаются как «спорные». Тем не менее, они широко используются, особенно когда надо написать какую-то простую программу, работающую с текстовыми данными.
Вопрос производительности iostreams не праздный. В частности, с проблемой производительности консольного ввода-вывода можно столкнуться в системах спортивного программирования, где даже применив хороший алгоритм, можно не пройти по времени только из-за ввода-вывода. Я также встречался с этой проблемой при обработке научных данных в текстовом формате.
Сегодня в комментариях у посту возникло обсуждение о медленности iostreams. В частности, freopen пишет
Забавно смотреть на ваши оптимизации, расположенные по соседству со считыванием через cin 🙂
Можно заменить на getchar_unlocked() для *nix или getchar() для всех остальных.
getchar_unlocked > getchar > scanf > cin, где «>» означает быстрее.
В этом посте я развею и подтвержу некоторые мифы и дам пару рекомендаций.
Все измерения в этом посте приведены для системы Ubuntu 14.10 с компилятором GCC 4.9.1, компилировалось с ключами
Запуск проводился на ноутбуке с процессором Intel Core2 Duo P8600 (2.4 ГГц).
Постановка задачи
В спортивном программировании, как и в UNIX-way, обычно входные данные подаются на входной поток. Итак, задача:
На входной поток (stdin) поступает много неотрицательных целых чисел по одному на строке. Программа должна вывести максимальное из входных чисел.
Сформируем входные данные
В файл data мы записали 10 миллионов последовательных целых чисел, общим объёмом 76 мегабайт.
Запускать программу мы будем так
1. scanf
Решим задачу с использованием старого доброго scanf.
Время работы: 1.41 c
2. Наивный std::cin
Теперь решим задачу самым простым способом при помощи iostreams:
Время работы: 4.41 c
Ого! Потоки оказались медленнее чем scanf в 3 раза! То есть выходит, что iostream оказываются действительно никуда не годится по скорости?
3. Быстрый std::cin
На самом деле, чтобы исправить ситуацию, достаточно добавить в программу одну единственную строчку. В самом начале функции main вставим:
Все последующие варианты с использованием std::cin будут использовать эту оптимизацию.
4. Наивный std::istringstream
Помимо ввода из файла, стандартная библиотека предоставляет также классы для ввода из строки с таким же интерфейсом. Посмотрим, насколько это медленно. Будем читать из входного потока по одной строке, а затем парсить её с помощью std::istringstream :
Время работы: 7.21 c
Очень медленно!
5. Переиспользование std::istringstream
Может показаться удивительным, но самое медленное в istringstream — это его создание. А мы создаём для каждой входной строки заново. Попробуем переиспользовать один и тот же объект:
Обратите внимание, что нужны 2 вызова — clear, чтобы сбросить флаги состояния, и str, чтобы задать новый буфер, из которого будет происходить чтение.
Время работы: 2.16 c
Это другое дело. Это ожидаемо медленнее, чем чтение напрямую из std::cin (данные проходят 2 раза через классы потоков), но не катастрофично.
6. Хотим ещё быстрее! (getchar/getchar_unlocked)
7. C++11: std::stoi
Время работы: 1.04 c
Это самый быстрый стандартный способ чтения целых чисел. (А для чисел с плавающей точкой есть аналогичные функции stof/stod).
8. Бонус: Чтение большими блоками + Boost::Spirit
Время работы: 0.18 c
Это рекорд!
Результаты и советы
Время работы:
No | Метод | GCC 4.9.1 | clang 3.5.0 + libc++ | GCC 100M* |
---|---|---|---|---|
1 | scanf | 1.41 | 1.48 | |
2 | std::cin | 4.41 | 13.30 | |
3 | std::cin и std::ios::sync_with_stdio(false) | 1.33 | 13.24 | |
4 | std::istringstream | 7.21 | 9.16 | |
5 | std::istringstream с переиспользованием | 2.16 | 7.92 | |
6a | getchar | 0.82 | 0.84 | 9.14 |
6b | getchar_unlocked | 0.28 | 0.26 | 2.94 |
7 | std::getline + std::stoi | 1.04 | 3.53 | 10.8 |
8 | Большой блок + Boost::Spirit | 0.18 | 1.67 | 1.90 |
* — Измерения на файле со 100 миллионами чисел (размер файла 848 мегабайт).
Рекомендации:
Update 1. По совету Lol4t0 добавлен метод номер 7.
Update 2. В таблицу добавлены времена выполнения на clang+libc++ (версия 3.5.0, выполнялось на той же системе). Видно, что производительность потоков очень плохая, да к тому же трюк с выключением синхронизации не работает. В результате stoi оказывается в 2 раза медленнее чем scanf.
Update 3. Добавлен вариант номер 8: чтение большими блоками и разбор с помощью Boost::Spirit. И это чемпион!
7.16 – std::cin и обработка недопустимого ввода
При написании программ вы всегда должны учитывать, как пользователи (непреднамеренно или наоборот) будут использовать ваши программы некорректно. Хорошо написанная программа будет предвидеть, как пользователи будут использовать ее неправильно, и либо аккуратно обработает эти случаи, либо вообще предотвратит их появление (если это возможно). Программа, которая хорошо обрабатывает случаи ошибок, называется надежной.
Чтобы обсудить, как std::cin и operator>> могут давать сбой, сначала полезно немного узнать, как они работают.
Когда мы используем operator>> для получения пользовательского ввода и помещения его в переменную, это называется «извлечением». Соответственно, в этом контексте оператор >> называется оператором извлечения.
При использовании оператора извлечения происходит следующая процедура:
Извлечение завершается успешно, если из входного буфера извлечен хотя бы один символ. Любые неизвлеченные входные данные остаются во входном буфере для дальнейшего извлечения. Например:
Извлечение не выполняется, если входные данные не соответствуют типу переменной, в которую они извлекаются. Например:
Проверка ввода
Процесс проверки того, соответствуют ли пользовательские входные данные тому, что ожидает программа, называется проверкой ввода.
Есть три основных способа проверки ввода:
Поскольку строки не имеют никаких ограничений на ввод символов, извлечение гарантированно завершится успешно (хотя помните, что std::cin прекращает извлечение на первом неведущем пробельном символе). После того, как строка введена, программа может проанализировать эту строку, чтобы узнать, корректна она или нет. Однако анализ строк и преобразование вводимых строк в другие типы (например, числа) может быть сложной задачей, поэтому это делается только в редких случаях.
Чаще всего мы позволяем std::cin и оператору извлечения выполнять эту тяжелую работу. В этом методе мы позволяем пользователю вводить всё, что он хочет, заставляем std::cin и operator>> попытаться извлечь данные и справиться с последствиями, если это не удастся. Это самый простой способ, о котором мы поговорим ниже.
Пример программы
Рассмотрим следующую программу-калькулятор, в которой нет обработки ошибок:
Эта простая программа просит пользователя ввести два числа и математический оператор.
Теперь подумайте, где неверный ввод пользователя может нарушить работу этой программы.
Сначала мы просим пользователя ввести несколько чисел. Что, если он введет что-то, отличающееся от числа (например, ‘q’ )? В этом случае извлечение не удастся.
Во-вторых, мы просим пользователя ввести один из четырех возможных символов. Что, если он введет символ, отличный от ожидаемых? Мы сможем извлечь входные данные, но пока не обрабатываем то, что происходит после.
Типы недопустимых входных текстовых данных
Обычно мы можем разделить ошибки ввода текста на четыре типа:
Таким образом, чтобы сделать наши программы устойчивыми, всякий раз, когда мы запрашиваем у пользователя ввод, мы в идеале должны определить, может ли произойти каждый из вышеперечисленных возможных вариантов, и если да, написать код для обработки этих случаев.
Случай ошибки 1: извлечение успешно, но входные данные не имеют смысла
Это самый простой случай. Рассмотрим следующий вариант выполнения приведенной выше программы:
Решение здесь простое: выполните проверку ввода. Обычно она состоит из 3 шагов:
Как видите, мы используем цикл while для бесконечного цикла до тех пор, пока пользователь не предоставит допустимые данные. Если он этого не делает, мы просим его повторить попытку, пока он не даст нам правильные данные, не закроет программу или не уничтожит свой компьютер.
Случай ошибки 2: извлечение успешно, но с посторонними входными данными
Рассмотрим следующий вариант выполнения приведенной выше программы:
Как думаете, что будет дальше?
Программа выводит правильный ответ, но форматирование испорчено. Давайте подробнее разберемся, почему.
Хотя программа работает, выполнение запутано. Было бы лучше, если бы любые введенные посторонние символы просто игнорировались. К счастью, символы игнорировать легко:
Теперь наша программа будет работать, как ожидалось, даже если мы введем «5*7» при первом запросе ввода – 5 будет извлечено, а остальные символы из входного буфера будут удалены. Поскольку входной буфер теперь пуст, при следующем выполнении операции извлечения данные у пользователя будут запрашиваться правильно!
Случай ошибки 3: сбой при извлечении
Теперь рассмотрим следующий вариант выполнения нашей программы калькулятора:
Неудивительно, что программа работает не так, как ожидалось, но интересно, как она дает сбой:
и программа внезапно завершается.
Это очень похоже на случай ввода посторонних символов, но немного отличается. Давайте посмотрим подробнее.
Находясь в «режиме отказа», будущие запросы на извлечение входных данных будут автоматически завершаться ошибкой. Таким образом, в нашей программе калькулятора вывод запросов всё еще печатается, но любые запросы на дальнейшее извлечение игнорируются. Программа просто выполняется до конца, а затем завершается (без вывода результата потому, что мы не прочитали допустимую математическую операцию).
К счастью, мы можем определить, завершилось ли извлечение сбоем, и исправить это:
Давайте, интегрируем это в нашу функцию getDouble() :
Примечание. До C++11 неудачное извлечение не приводило к изменению извлекаемой переменной. Это означает, что если переменная была неинициализирована, она останется неинициализированной в случае неудачного извлечения. Однако, начиная с C++11, неудачное извлечение из-за недопустимого ввода приведет к тому, что переменная будет инициализирована нулем. Инициализация нулем означает, что для переменной установлено значение 0, 0.0, «» или любое другое значение, в которое 0 преобразуется для этого типа.
Случай ошибки 4: извлечение успешно, но пользователь выходит за пределы значения числа
Рассмотрим следующий простой пример:
Что произойдет, если пользователь введет слишком большое число (например, 40000)?
В приведенном выше случае std::cin немедленно переходит в «режим отказа», но также присваивает переменной ближайшее значение в диапазоне. Следовательно, x остается с присвоенным значением 32767. Дополнительные входные данные пропускаются, оставляя y с инициализированным значением 0. Мы можем обрабатывать этот вид ошибки так же, как и неудачное извлечение.
Примечание. До C++11 неудачное извлечение не приводило к изменению извлекаемой переменной. Это означает, что если переменная была неинициализирована, в случае неудачного извлечения она останется неинициализированной. Однако, начиная с C++11, неудачное извлечение вне диапазона приведет к тому, что переменной будет присвоено ближайшее значение в диапазоне.
Собираем всё вместе
Вот наш пример калькулятора с полной проверкой ошибок:
Заключение
При написании программы подумайте о том, как пользователи будут неправильно использовать вашу программу, особенно при вводе текста. Для каждой точки ввода текста учтите:
Вы можете использовать операторы if и булеву логику, чтобы проверить, являются ли входные данные ожидаемыми и осмысленными.
Следующий код очистит любые посторонние входные данные:
Следующий код будет проверять и исправлять неудачные извлечения или переполнение:
Наконец, используйте циклы, чтобы попросить пользователя повторно ввести данные, если исходные входные данные были недопустимыми.
Примечание автора
Проверка ввода важна и полезна, но она также делает примеры более сложными и трудными для понимания. Соответственно, на будущих уроках мы, как правило, не будем проводить никакой проверки вводимых данных, если они не имеют отношения к чему-то, чему мы пытаемся научить.
cin.get() и его друзья
Функция
int istream::get();
определенная в классе istream библиотеки по определению извлекает из входного потока один символ и возвращает его целочисленный код. Популярность ей принес тот факт, что ее удобно использовать в программах с консольным интерфейсом, которые запускаются не из консоли, например, из проводника или из графического интерфейса IDE. После завершения работы программы мы не сможем увидеть ее финальный вывод, поскольку выполнится инструкция return и программа завершится, закрывая «за собой» консольное окно.
Функция get() же стандартного потока ввода cin заставляет систему ожидать ввода пользователем любого символа, который она считывает, и программа завершается.
Проблема в том, что работает cin.get() далеко не всегда. Почему? Рассмотрим ситуацию издалека.
Начнем с того, что же такое поток (stream). Ненаучным языком говоря, поток — последовательность символов. Источником символов может служить в частности клавиатура. Символы идут один за другим:
Надо сказать, что если по каким-то причинам из потока прочитаны не все символы до конца строки (символа ‘\n’ ) включительно, то после операции чтения поток не будет пустым. Два самых распространенных способа дают нам два хороших примера.
1)
cout
Уже рассматривавшаяся выше функция get() читает из потока один символ, так что если мы ввели несколько символов, то она оставит за собой непустой поток. И следующий вызов cin.get() будет обречен на «фиаско»: программа вместо того, чтобы остановиться и ждать пользовательского ввода, прочитает из входного потока очередной символ, оставшийся от предыдущего ввода, и продолжит свое выполнение. Или завершится «без спроса». Напомню, что get() возвращает приведенный к типу int код введенного символа, поэтому мы может использовать его в вышеприведенной инструкции — код просто будет выведен на экран.
Собственно вывод: если в конце программы cin.get() не ждет пользовательского ввода, значит, вы оставили за собой непустой входной поток.
2)
int a;
cin>>a;
Перегруженный оператор сдвига, использующийся для ввода данных из потока, который в свою очередь перегружен для работы с целыми числами (так как вызван с параметром a типа int ), считывает символы, являющиеся десятичными цифрами, до тех пор, пока не встретит нецифровой символ. Это может быть пробел, буква, табуляция, конец строки, и так далее.
Все эти символы остались в потоке. Даже если вы просто ввели число и нажали Enter, символ ‘\n’ остался в потоке.
Следующая шуточная программка позволяет воочию увидеть «обидчика». Скомпилируйте ее, запустите и введите, к примеру,
using namespace std;
Теперь, когда проблема очевидна, рассмотрим ее возможные решения. В порядке увеличения их сложности.
1)
Функция
В нашем случае мы стремимся отбросить максимальное число символов, которое может содержать поток, до первого перевода строки включительно.
cin.ignore(numeric_limits ::max(), ‘\n’);
2)
streambuf* istream::rdbuf() const;
streamsize streambuf::in_avail();
Второй параметр функции ignore() имеет значение по умолчанию, что делает его необязательным, и мы его просто опускаем при вызове.
3)
int istream::sync();
Использование функции sync() — путь наименьшего сопротивления. Она просто очищает поток от имеющихся в нем символов.
Почему этот способ самый сложный — поищите в Гугле (если есть желание). А я в это время порекомендую Вам выбрать более понравившийся из первых двух и пойду заниматься более полезными делами 😉
Хотя нет, напоследок еще расскажу о функции
Русские Блоги
Очистить входной буфер в C ++
Введение в проблему
Те, кто использовал C ++, понимают, что случайные ошибки ввода будут возникать в процессе использования ввода, что приведет к тому, что последующий ввод будет недействительным. C ++ продолжит использовать предыдущий случайный ввод, что приведет к неправильному запуску программы.
Входной буфер
Все данные, вводимые с клавиатуры, будь то символы или числа, сначала сохраняются в буфере в памяти, называемом буфером клавиатуры, или для краткости Входной буфер или Входной поток 。
Когда ввод с клавиатуры заканчивается, входные данные сохраняются во входном буфере, и cin Функция считывает данные прямо из входного буфера. Этот буферный механизм предусматривает, что только после получения клавиши Enter все входные данные будут отправлены в cin функция. Возврат каретки отмечает завершение одного ввода. Если данных недостаточно, он будет ждать, пока пользователь продолжит ввод; если данные избыточны, избыточные данные будут сохранены в буфере входного потока для следующего использования.
————————————————
Ссылка для справки: https://blog.csdn.net/zhao708981169/article/details/36392681
Как решить
1.cin.clear()
Это значение каждого идентификатора статуса:
(перевод youdao)
Например: определите, что вводимая переменная является целым числом, но если мы введем другие символы, произойдет ошибка. cin Есть способ обнаружить эту ошибку: cin.rdstate() ; когда cin.rdstate() Если он возвращает 0 (т.е. ios :: goodbit), это означает, что ошибки нет, и вы можете продолжить вход или работу. Если он возвращает 2, возникает нефатальная ошибка (например, ios :: failbit), и вы не можете продолжить войти или работать.
в заключении:
cin.clear () может сделать биты состояния нормальными, но не может очистить входной буфер.
2.cin.sync()
Не все времена эффективны, вы можете обратиться к этому сообщению в блоге для получения подробной информации:
Грубо говоря: для входных потоков стандартных библиотек, таких как std :: cin, вызов sync () является поведением, «определяемым реализацией». Если ожидаемый эффект не достигается, вы можете проверить свой собственный компилятор (реализация стандартной библиотеки) документации, должно быть описание того, какое поведение он использует.
Просто посмотрите определение функции синхронизации? В исходном документе функция синхронизации определяется следующим образом:
Но обратите внимание на это предложение в китайском документе:
Для того, чтобы следующая операция чтения зафиксировала любые изменения, которые могли быть внесены в связанную входную последовательность после того, как буфер потока окончательно заполнил свою область сбора данных. Для этого sync () может очистить область сбора данных, или заполнить ее, или ничего не делать. Заметным исключением является Visual Studio, где эта операция сбрасывает необработанный вывод при вызове со стандартным потоком ввода.
Почему MSVC, который я использовал, является исключением, он не ароматный!
Как видите, ввод «#» по-прежнему не может продолжаться, а значение a по-прежнему равно 0.
Снова измените код, пусть другая строка получит «#» в буфере, чтобы увидеть, действительно ли он не очищен:
Как видите, я не ввел следующую строку, но получил символ «#» в буфере, и я могу продолжить переход к следующему циклу.
в заключении
В разных компиляторах функция синхронизации реализована по-разному, что делает ее функции разными.Я использую MSVC, который не может выполнять функцию очистки буфера.
3.cin.ignore()
numeric_limits ::max() Но это climits Максимальное значение, используемое потоком, определенное файлом заголовка, вы также можете заменить его на достаточно большое целое число.
————————————————
Исходная ссылка: https://blog.csdn.net/selina8921/article/details/79067941
Видно, что после вызова функции cin.ignore () str не может извлечь неправильный символ «#», что означает, что содержимое буфера игнорируется, но str по-прежнему не может быть вошел. Причина кроется вНе вызывала cin.clear () для восстановления бита состояния。
Для второго теста введите последовательно: ###
Видно, что без добавления cin.clear () str все равно не сможет получить содержимое буфера, даже если cin.ignore () игнорирует только 1 символ и не может продолжать ввод.
Добавьте вызов функции cin.clear () перед cin.ignore (), выполните третий тест и введите: ###,1,my
Видно, что после вызова функции cin.ignore () str может извлечь неправильный символ «##», что означает, что содержимое буфера игнорируется, но только один символ. игнорируется. В следующий раз вы можете продолжить ввод a и str.
в заключении:
Чтобы сделать буфер полностью пустым, самое безопасное решение: