Я только начинающий программер... Потому не надо писать типа "... вот ламо... rtfm... " Я лишь прошу консультации.
Прога — консолька, пишу на Visual Studio 2005.
Собственно столкнулся с такой неприятной неприятностью. Прога у меня считывает вводимую инфу, предположим какое-либо число (int), как сделать проверку введенных символов, на факт присутствия в них символов не относящихся к цифрам? Просто если ввести буквы, а прога считывает в интовскю переменную, то она вылетает...
Здравствуйте, maksqwe, Вы писали:
Вот вам костыль из моей далекой молодости
Вообще — он ужасен, но работает(сделан специально под виндовый cmd)
см. коментарии.
#ifndef _1251_to_866_H
#define _1251_to_866_H
#include <iostream>
#include <string>
#include <stdexcept>
// cp1251(в ней обычно MSVC исходник сохраняет)->cp866(виндовая консолька)
std::string
_r(const char* str)
{
int i = 0;
int y = 0;
char z;
std::string tmp;
while (*(str + i) != '\0')
{
y = z = *(str + i);
if ((y <= -17) && (y >= -64))
z = 192 + y;
if ((y <= -1) && (y >= -16))
z = 240 + y;
tmp+=z;
i++;
}
return tmp;
}
struct __resurrect_cin
{
__resurrect_cin(){}
static void
resurrect()
{
if(!std::cin)
{
std::cin.clear();
std::cin.ignore(4096,'\n');
throw(std::runtime_error(_r("Ошибка ввода. Повторите.\n")));
}
}
};
const __resurrect_cin resurrect_cin;
std::istream&
operator>>(std::istream& in, const __resurrect_cin&)
{
__resurrect_cin::resurrect();
return in;
}
#endif
Здравствуйте, maksqwe, Вы писали:
M>Я только начинающий программер... Потому не надо писать типа "... вот ламо... rtfm... " Я лишь прошу консультации.
M>Прога — консолька, пишу на Visual Studio 2005. M>Собственно столкнулся с такой неприятной неприятностью. Прога у меня считывает вводимую инфу, предположим какое-либо число (int), как сделать проверку введенных символов, на факт присутствия в них символов не относящихся к цифрам? Просто если ввести буквы, а прога считывает в интовскю переменную, то она вылетает...
M>Прошу помочь!! Буду премного благодарен!
Для этого нужно написать собственную функцию посимвольного ввода с проверкой на легальность символов.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, maksqwe, Вы писали:
M>... Прога у меня считывает вводимую инфу, предположим какое-либо число (int), как сделать проверку введенных символов, на факт присутствия в них символов не относящихся к цифрам? Просто если ввести буквы, а прога считывает в интовскю переменную, то она вылетает...
В некоторых случаях можно так:
#include <iostream>
using namespace std;
int main()
{
int i;
if (cin >> i)
cout << "i = " << i << '\n';
else
cout << "error\n";
}
Здравствуйте, maksqwe, Вы писали:
M>Прога — консолька, пишу на Visual Studio 2005. M>Собственно столкнулся с такой неприятной неприятностью. Прога у меня считывает вводимую инфу, предположим какое-либо число (int), как сделать проверку введенных символов, на факт присутствия в них символов не относящихся к цифрам? Просто если ввести буквы, а прога считывает в интовскю переменную, то она вылетает...
Лучше всего проверять не посимвольно, а положиться на стандартные языковые средства работы со строками и распознавания в них чисел. Наверное, самый «правильный» способ такой:
std::string s;
. . .
// как-то получили строку в s. Например, std::cin >> s.
. . .
int value = boost::lexical_cast<int>(s); // can throw boost::bad_lexical_cast
. . .
// если вводим из потока (stream >> s), то можно и сразу проверять - см. совет г-на Лаптева выше.
Здесь в случае «лишних» символов в строке будут вылетать исключения. Следовательно, программа унаследует все преимущества и недостатки последних. Если, как я понимаю, строки должны являться числами (а иначе вылетаем с ошибками), то исключения — то, что надо.
Здравствуйте, maksqwe, Вы писали:
I>>В некоторых случаях можно так:
I>>
I>>#include <iostream>
I>>using namespace std;
I>>int main()
I>>{
I>> int i;
I>> if (cin >> i)
I>> cout << "i = " << i << '\n';
I>> else
I>> cout << "error\n";
I>>}
I>>
M>После чего бесконечно повторяется следующая функция... Как сделать что бы этого не происходило???
вот так — читай сюда:
Состояния потока
Каждый поток (и стандартные в том числе) в каждый момент времени находится в некотором состоянии. Эти состояния имеют названия good, bad, fail и eof (end-of-file). Сами состояния определены в классе ios_base как целые статические константы. Эти константы называют флагами состояния потока [1-27.4.2.1.3].
Значения флагов зависят от реализации (показанные значения флагам присвоены в системе C++ Builder 6), однако имена определены в стандарте, поэтому именно так флаги состояния называются во всех без исключения системах. В программе имена флагов состояния нужно записывать с префиксом, например
std::ios_base::eofbit
С таким же успехом можно использовать класс, производный от ios_base. В частности, в текстах программ стандартной библиотеки часто встречается более короткий префикс
std::ios::eofbit
В базовом классе ios_base определено поле
iostate _M_iostate; // библиотека STLport
ПРИМЕЧАНИЕ
Такое имя поле имеет в системе Borland C++ Builder 6. В другой реализации имя поля может быть другим.
В этом поле сохраняются флаги состояния во время работы программы. Это поле мы можем прочитать методом
iostate rdstate();
Установить любой флаг можно методом
void setstate(iostate flag);
Для установки нескольких флагов, естественно, нужно воспользоваться битовыми операциями, например
setstate(std::ios::eofbit | std::ios::failbit);
Система ввода/вывода С++ предоставляет несколько методов, с помощью которых мы всегда можем узнать состояние потока:
bool good() const; // следующая операция может выполнятьсяbool eof() const; // виден конец вводаbool fail() const; // следующая операция не выполняетсяbool bad() const; // поток испорчен
Если после выполнения некоторой операции поток находится в состоянии good, то это хорошая ситуация: во время предыдущей операции не произошло никаких непредвиденных событий и может быть выполнена следующая операции ввода/вывода. В остальных случаях следующая операция выполнена не будет.
Состояние fail и состояние bad являются состояниями ошибки потока. Если поток находится в одном из этих состояний, то операции обмена не выполняются. Состояние bad включает в себя состояние fail: когда поток находится в состоянии bad, он находится и в состоянии fail; обратное неверно.
Если поток находится в состоянии fail, то операция ввода/вывода завершилась неудачно, однако поток не испорчен и никакие символы не потеряны. Обычно это состояние устанавливается при ошибках форматирования в процессе чтения — например, программа пытается прочитать целое число, а первый же символ является недопустимым (буква или знак операции). Поток из состояния fail мы можем вернуть в нормальное состояние с помощью метода clear(), например
if (stream.fail()) stream.clear();
После этого можно выполнять операции обмена — опять до очередной ошибки. Метод clear() перегружен и позволяет не только сбросить, но и затем установить нужные флаги состояния. Прототип этого метода
void clear(iostate flag);
Состояние bad – это более тяжелое состояние. Флаг badbit указывает на неработоспособность потока данных или потерю данных. Когда поток в состоянии bad, ни в чем нельзя быть уверенным, поэтому в таких случаях лучше завершать выполнение программы.
Состояние eof может возникнуть только при операции чтения. Для стандартного потока ввода это состояние возникает при вводе комбинации клавиш <Ctrl> + <z>. Для файлов это состояние устанавливается при первой попытке чтения после последнего байта файла. При этом поток тоже переводится в состояние fail.
Состояние потока и логические условия
Состояние потока можно проверять непосредственно в условиях if и while. Например, ввод массива целых значений можно выполнять в цикле таким образом
int aa[10] = {0};
i = 0;
while(cin >> aa[i++]);
Цикл завершается по одной из двух причин:
1. Создалась ситуация «end-of-file»; для входного потока это эквивалентно нажатию комбинации клавиш <Ctrl> + <z>;
2. В потоке встретился символ, не соответствующий типу вводимой переменной; в этом случае поток переводится в состояние fail;
Например, мы набрали на клавиатуре следующую последовательность символов
1<пробел>2<пробел>3<пробел>4<пробел>5e6<enter>
Тогда элементы массива а будут иметь следующие значения:
Символ ‘e’после цифры 5 не является допустимым для целого числа, поэтому ограничивает ввод.
В условиях можно задавать и явный вызов методов. Например, мы хотим посчитать количество символов ‘\n’, которым оканчиваются вводимые строки. Это можно сделать, используя метод get():
int nl = 0;
while(cin.get(ch)) // читать все символы, в том числе пробельные
{ if(ch=='\n') nl++;
cout.put(ch);
}
Так как «неправильных» символов не существует, цикл можно завершить только вводом комбинации <Ctrl> + <z>. Можно использовать и другую форму метода get():
int nl = 0;
while(ch = cin.get() && ch != EOF) // читать все символы
{ if(ch=='\n') nl++;
cout.put(ch);
}
Константа EOF помещается в ch при нажатии комбинации <Ctrl> + <z>.
В библиотеке реализована логическая операция operator!, с помощью которой можно проверить аварийное состояние потока, например
Обратите внимание, выражение ввода заключено в скобки. Это делать необходимо, так как в соответствии с приоритетом операций выражение без скобок интерпретируется как
(!cin) >> x
Естественно, ввод и проверку состояния потока можно разделить, например
Во всех приведенных примерах, на месте cin может стоять любой поток.
При разделении ввода и проверки можно использовать явный вызов соответствующего метода. Например, таким способом мы можем проверять наступление ситуации «end-of-file» для потока, связанного с файлом.
while (!from.eof()) // явная проверка на конец файла
{ getline(from, s); // чтение строки из потока from
cout << s << endl;
}
Однако тут возможны сюрпризы. По умолчанию ограничителем строки является символ ‘\n’, который записан в конце каждой строки. В данном случае при считывании последней строки ситуации конца файла не возникает — она возникает только при попытке ввода за концом файла. Поэтому цикл выполнится лишний раз и на экране дважды появится последняя строка.
Правильная последовательность операторов может быть такой
getline(from, s); // чтение строки из потока fromwhile (!from.eof()) // явная проверка на конец файла
{ cout << s << endl;
getline(from, s); // чтение строки из потока from
}
Другой вариант — бесконечный цикл с проверкой «end-of-file» внутри цикла
while (true)
{ getline(from, s); // чтение строки из потока fromif(from.eof()) break; // явная проверка на конец файла
cout << s << endl;
}
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, Roman Odaisky, Вы писали:
RO>Лучше всего проверять не посимвольно, а положиться на стандартные языковые средства работы со строками и распознавания в них чисел. Наверное, самый «правильный» способ такой: RO>
RO>std::string s;
RO>. . .
RO>// как-то получили строку в s. Например, std::cin >> s.
RO>. . .
RO>int value = boost::lexical_cast<int>(s); // can throw boost::bad_lexical_cast
RO>
RO>Здесь в случае «лишних» символов в строке будут вылетать исключения. Следовательно, программа унаследует все преимущества и недостатки последних. Если, как я понимаю, строки должны являться числами (а иначе вылетаем с ошибками), то исключения — то, что надо. RO>Boost: www.boost.org RO>2 All: я понимаю, что Boost — то еще развлечение для начинающего, но надо начинать с лучшего, верно?
Тогда уж лучше использовать стандартные строковые потоки:
Строковые потоки
В отличие от строковых потоков, реализованных в библиотеке <cstdio>, которые могут быть только входными или только выходными, строковые потоки объектно-ориентированной библиотеки [1-27.7] могут быть еще и двунаправленными. Кроме того, нам нет необходимости резервировать память для строки при работе с ними. И наконец, со строковыми потоками можно использовать все средства форматирования или собственные написанные манипуляторы.
Чтобы использовать строковые потоки, мы должны прописать в программе оператор
#include <sstream>
После этого в программе можно объявлять объекты-строковые потоки трех видов:
входной istringstream [1-27.7.2];
выходной ostringstream [1-27.7.3];
двунаправленный stringstream [1-27.7.4];
Чтобы продемонстрировать возможности строковых потоков, перепишем примеры 10.4 и 10.6. Первый пример (листинг 10.32) — аналог примера 10.4 — демонстрирует возможности выходных строковых потоков.
Листинг 10.32. Выходной строковый поток
#include <iostream>
#include <sstream>
using namespace std;
int main()
{ char s[] = "computer", c = 'L';
int i = 35;
float fp = 1.7320534f;
ostringstream os;
/* Форматирование и вывод данных */
os << "String: " << s << "; "
<< "Character: " << c << "; "
<< "Integer: " << i << "; "
<< "Real: " << fp<< "; "
<< endl;
cout << "Output:" << '\n' << os.str() << endl;
cout << "character count = " << os.str().length() << endl;
int ch = getchar(); // чтобы видеть результатreturn EXIT_SUCCESS;
}
Эта программа делает абсолютно то же самое, что и программа в примере 10.4. Но посмотрите, насколько она стала проще и надежней! Во-первых, программисту нет необходимости резервировать память для формируемой строки — строковый поток все делает во внутреннем буфере. Во-вторых, нет необходимости следить за позицией внутри буфера (как это делается в примере 10.4 — переменная j) — все делает сам поток (вернее, сам буфер, поскольку это объект типа string). Внутренний буфер всегда можно получить, вызвав для потока метод str().
Выходной поток может быть создан в режиме дозаписи (как и файл). Тогда он должен быть проинициализирован существующей строкой, например,
string s = “Шестнадцатеричное число = “;
ostringstream os(s, ios::out|ios::app);
os << hex << 123;
cout << os.str() << endl;
В результате работы этого фрагмента на экран будет выдано
Шестнадцатеричное число = 7B
Сама строка s остается без изменений.
Вместо двух потоков используем один двунаправленный поток (листинг 10.33).
Листинг 10.33. Аналог примера 10.6
{ string names[4] = {"Peter", "Mike", "Shea", "Jerry"};
string temp[4];
string name;
int age;
long salary;
stringstream strm; // двунаправленный поток
srand( (unsigned)time(NULL)); // инициализация датчика случайных чисел
/* создание данных name, age и salary */for (int loop=0; loop < 4; ++loop)
strm<<names[loop]<<' '<<rand()%10+20<<' '<<rand()%5000+27500L<< endl;
/* вывод шапки */
cout << setw(4) << "#" << " | "
<< left << setw(20) << "Name" << " | "
<< right << setw(5) << "Age" << " | "
<< right << setw(15) << "Salary" << endl;
cout << " --------------------------------------------------\n";
strm.seekg(0); // перевод головки чтения/записи в начало потока for (loop=0; loop < 4; ++loop)
{ strm >> name >> age >> salary; // ввод name, age и salary
cout << right << setw(4) << (loop + 1) << " | "
<< left << setw(20) << name << " | "
<< right << setw(5) << age << " | "
<< setw(15) << salary << endl;
}
}
Эта программа сначала пишет в поток strm. Потом из того же потока выполняется считывание и вывод на экран.
Перед считыванием устанавливается позиция чтения методом seekg() [1-27.6.1.3]. В отличие от библиотеки <cstdio> в объектно-ориентированной библиотеке позиционирование можно выполнять и для строковых, и для файловых потоков. Это нельзя делать только для стандартных потоков. Набор методов, связанных с позиционированием, значительно расширен и намного удобнее, чем функции библиотеки <cstdio>. Мы рассмотрим методы позиционирования далее.
Входной строковый поток можно использовать, чтоб реализовать считывание чисел из поля фиксированной длины. Не будем писать манипулятор с аргументами, а напишем простой шаблон функции (листинг 10.34).
Листинг 10.34. Ввод из поля фиксированной длины
template <class T>
void fixedread(istream &in, T &t)
{ if (in.width() > 0) // ширина поля установлена
{ string field;
in >> field; // ввод по ширине или до пробела
istringstream strm(field); // входной строковый потокif (!(strm >> t) // ввод их строкового потока
in.setstate(ios::failbit); // ошибки ввода
}
else// ширина поля по умолчанию
in >> t; // ввод в переменную
}
Если ширина поля ввода установлена (то есть, не равна нулю), то объявляется строка для ввода и ввод выполняется в нее. Затем этой строкой инициализируется строковый поток. Обратите внимание на то, что ввод из строкового потока проверяется на корректность и в случае ошибки устанавливается флаг failbit для входного потока-параметра.
Использовать эту функцию можно так:
int k = 0; cin.width(3);
fixedread(cin, k);
Если мы наберем на клавиатуре 1234, то в переменную k попадет число 123. При наборе строки «56 4567» в переменную k попадает число 56.
И кстати об исключениях:
Потоки и исключения
По умолчанию при ошибках ввода/вывода исключения не генерируются. Однако можно указать, при установке каких флагов состояния должно генерироваться исключение. В состав класса basic_ios [1-27.4.4] входит метод exceptions(), который и позволяет это сделать. Прототип метода следующий
void exceptions(iostate flags);
Задать генерацию исключения при установке флага ios::badbit можно так:
stream.exceptions(ios::badbit);
Флаги, как обычно, можно комбинировать, например
stream.exceptions(ios::badbit|ios::failbit);
Исключения будут генерироваться не только во время операций ввода/вывода, но и при установке флагов методами clear() и setstate().
Если аргумент равен 0 или ios::goodbit, исключения генерироваться не будут.
Вызов без аргумента возвращает текущие флаги, при установке которых генерируется исключение, например
ios_base::iostate flags = stream.exceptions();
Если возвращается goodbit, исключения не генерируются.
Генерируемые исключения являются объектами класса ios_base::failure [1 27.4.2.1.1], который является наследником класса exception (см. главу 4). Класс ios_base::failure имеет следующую структуру:
class ios_base::failure : public exception
{ public:
explicit failure(const string& msg);
virtual ˜failure();
virtual const char* what() const throw();
};
Как видите, сложного ничего нет.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
On Sun, 24 Dec 2006 16:42:35 +0500, maksqwe <62114@users.rsdn.ru> wrote:
> Прога — консолька, пишу на Visual Studio 2005. > Собственно столкнулся с такой неприятной неприятностью. Прога у меня > считывает вводимую инфу, предположим какое-либо число (int), как > сделать проверку введенных символов, на факт присутствия в них символов > не относящихся к цифрам? Просто если ввести буквы, а прога считывает в > интовскю переменную, то она вылетает...
string s;
cin >> s;
int i;
int err = sscanf(s, "%d", &i);
Здравствуйте, maksqwe, Вы писали:
M>Я только начинающий программер... Потому не надо писать типа "... вот ламо... rtfm... " Я лишь прошу консультации.
M>Прога — консолька, пишу на Visual Studio 2005. M>Собственно столкнулся с такой неприятной неприятностью. Прога у меня считывает вводимую инфу, предположим какое-либо число (int), как сделать проверку введенных символов, на факт присутствия в них символов не относящихся к цифрам? Просто если ввести буквы, а прога считывает в интовскю переменную, то она вылетает...
M>Прошу помочь!! Буду премного благодарен!
для этого нужно просто считывать не в числовую переменную а в строковую. после -- конвертировать ее в числовое значение -- ошибки не будет. если человек введёт "вася", то оно просто конвертирует его безошибочно в значение 0.
более сложный вариант: -- читать посимвольно и пропускать только символы от "0" до "9".
Здравствуйте, maksqwe, Вы писали:
M>Прога — консолька, пишу на Visual Studio 2005. M>Собственно столкнулся с такой неприятной неприятностью. Прога у меня считывает вводимую инфу, предположим какое-либо число (int), как сделать проверку введенных символов, на факт присутствия в них символов не относящихся к цифрам?