Где вас пустили по 16-мильному крюку(корректный ввод дабла в Си)
Запись от -=ЮрА=- размещена 08.04.2012 в 14:25
Показов 13732
Комментарии 30
|
Данная моя запись относиться к данному блогу https://www.cyberforum.ru/blogs/42536/blog278.html а именно к записи Пример безопасного чтения double Да fasked постарался и его блог действительно заслуживает внимания, оно НО можно писать эффективней и красивей! Существуют два подводных камня в вводе выводе double 1-й облом программы при вводе параметра с типом double на старых компиляторах без начальной инициализации этого параметра любым стохастическим числом. Поясняю
2-й печать printf параметра double. Это тот факт что при печати с ключом %lf , будет печататься всего 6-ть символов после запятой (вывод дефалтится до float). Так вот этот момент обходим посредством спецификатора длинны формата %.11f ну а ниже код безопасного ввода double верней с так называемой "защитой от некорректного ввода" Изначальный код
Вуаля - никаких flush stderr и прочей на мой взгляд не нужной и путающей простого обывателя байды! Желающие могут сравнить длину кодов "Безопасного ввода дабла" и выбрать оптимальный... Всех читающих настоятельно прошу дочитать дискуссию ниже(ниже также представлена усовершенствованная версия кода, более компактная и функциональная, код дан в двух вариантах С и С++). Также по прочтению статьи предлагаю вам сделать свои выводы(они совсем необязательно должны быть в мою пользу, но вы в любом случае должны знать как писать код на 10 строк вместо 100-ни) Ниже доработанная версия кода с извещениями на различные ошибки ввода!
| ||||||||||||||||||||
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 30
Комментарии
-
> Желающие могут сравнить длину кодов "Безопасного ввода дабла"
Сравнил. Во всяком случае сравниваю с кодом из раздела "Чтение данных с консоли, используя scanf" (если ты имел в виду код из раздела "Пример безопасного чтения double", то это был всего лишь пример безопасного чтения с внятной диагностикой ошибки на примере double'а). Если смотреть на принципиальные места (поскольку твой код и код fasked'а выполняет немного разные вещи), то очевидно, что твой код длиннее. У fasked'а написано
у тебяC 1
scanf("%*[^\n]");
Поскольку ты мерилом крутости кода считаешь его размер, то твой код, очевидным образом, более хреновый. У тебя в двух местах стоит печать "Wrong input", что очевидным образом говорит о криво спроектированном кодеC 1 2
while(bufStub != '\n') scanf("%c",&bufStub);
Инициализация нулём - этого прикола я не понимаю. Пока кто-то не покажет конкретный код, который бы без обнуления отработал неправильно - не поверю
> Вуаля - никаких flush
Это потому что ты под виндой работаешь, а конкретно под виндой scanf работает таким образом, что перед своей работой сам делает flush. А вот на другой системе это может быть совсем не такЗапись от Evg размещена 08.04.2012 в 17:10
-
Запись от Evg размещена 08.04.2012 в 17:49
-
Evg, в любом случае я уверен можно спроектировать код существенно короче и эффективней чем у fasked. Писал проект за 3,5 минуты и ещё + 2,5 минуты на вторую версию и единственной целью ставил показать что важны вот эти два момента
а всё остальное детали реализации.
Я исповедую принцип: "нужно показать как можно короче но не совсем красиво", а у кого голова есть и руки на том месте может домыслить как надо. Мне пока лень показывать последний 3-й вариант (кода ещё меньше), а для С++ так вообще 4 строки вышло...
Если кто попросит выложу супер короткие и эффективные варианты не уступающие функционалом функционалу fasked. Пока кроме тебя на этот мой блог никто ещё не обратил внимание(в плане комментариев, которые олицетворяют собой заинтересованность). По поводу кода я уверен что ты и сам можешь всё домыслитьЗапись от -=ЮрА=- размещена 09.04.2012 в 21:16
-
> что важны вот эти два момента
С этим я согласен и не спорю. Я спорю с другим. fasked в своей статье показывал совершенно другое, а не то, с чем ты пытаешься сравниться путём измерения длины пиписек. Но ты до сих пор этого не понял.
> По поводу кода я уверен что ты и сам можешь всё домыслить
Конечно могу. Как и многие могут. Только те, кто могут - им твоя статья нафиг не нужна, потому что они и так это знают. А тем, кому бы статья оказалась полезной, вместо полезной статьи видят кучу мусора, написанного в спешке и рекомендации чего-то там домыслить. В итоге оказывается, что ты написал статью сам для себя, потому что кроме тебя она никому не интереснаЗапись от Evg размещена 10.04.2012 в 10:09
-
Хорошо для таких начинающих даю два варианта "безопасного ввода дабла"
Вариант 1 Си-шный ввод вывод
Отработка кодаC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
#include <stdio.h> int main() { char bufStub = 0; double param = 0;//Вот важный момент начальная инициализация! while(1) { bufStub = 0; printf("Enter double : "); if((scanf("%lf%c",¶m,&bufStub) != 2) || bufStub != '\n') { printf("Wrong input\n"); //Данный цикл отработает всегда while(bufStub != '\n') scanf("%c",&bufStub); } else printf("Your input %.11f\n",param);//Тоже мало кто знает что %.11f позволятет } return 0; }
Вариант 2 С++ ввод вывод (как бонус оснастил код самописной функцией вывода на экран дабла с любой задаваемой пользователем точностью, такого уж точно в блоге fasked я не увидел)C 1 2 3 4 5 6 7 8 9 10 11 12 13
Enter double : 123.123.123 Wrong input Enter double : fff Wrong input Enter double : 12f Wrong input Enter double : fgfdg Wrong input Enter double : 123.ffgff Wrong input Enter double : 123.12345678912 Your input 123.12345678912 Enter double :
Отработка кодаC++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#include <iostream> #include <iomanip> #include <cmath> using namespace std; //Это небольшой самописный велосипед //для корректного вывода дабла с необходимой точностью char cout_double(double v, unsigned int p) { double ipart, fpart; fpart = modf(v,&ipart); cout<<setprecision(0)<<ipart<<".";//Выводим целую часть for(unsigned int i = 0; i < p; i++) { fpart = modf(fpart*10,&ipart); cout<<setprecision(0)<<ipart; } return '\n'; } int main() { double param;//Для С++ проблемм с вводом дабла без начального инита //мной за 10 лет написания программ замечено не было, так что инит нулём опустил while(1) { cout<<"Enter double : "; if(!(cin>>param) || cin.get() != '\n') { printf("Wrong input\n"); cin.clear();//Сбрасываем флаги ошибки потока cin.sync();//Чиистим оставшееся от ввода некорректное содержимое } else { cout<<"Your input : "; cout<<cout_double(param,11); } } return 0; }
PS:C 1 2 3 4 5 6 7 8 9 10 11 12 13
Enter double : 123.123.123 Wrong input Enter double : fff Wrong input Enter double : 12f Wrong input Enter double : fgdgd Wrong input Enter double : 123.fdfgdfg Wrong input Enter double : 123.123456789123 Your input : 123.12345678912 Enter double :
Я ни с кем не пытаюсь сравниться, я такой как есть и мои знания таковы как они есть. Во многом я вижу как можно сократить до 10 строк то на что люди тратят 100 и как можно делать просто и сердито. С недавнего времени перерос этап когда хочется с кем-то равняться, вот и всё.
Данный блог создал с целью уведомить сограждан киберфорума, что есть варианты безопасного ввода дабла покрасивше и понятней предложенных ранее.Запись от -=ЮрА=- размещена 10.04.2012 в 20:03
-
Запись от Evg размещена 11.04.2012 в 10:21
-
Запись от Evg размещена 11.04.2012 в 10:23
-
Evg. что он делает не то? Дабл корректно не вводит???!Программа не печатает развёрнутое сообщение об ошибке, могу ввести и такое посредством FormatMessage, НО каково утилитарное предназначние развёрнутого соообщения об ошибке(в любом случае даже с FormatMessage код будет компактней). Сейчас мой код работает так, ввели неверно получили сообщение Wrong Input, лаконично и понятноЗапись от -=ЮрА=- размещена 11.04.2012 в 13:26
-
Запись от -=ЮрА=- размещена 11.04.2012 в 13:29
-
Ты занимаешься юридическим буквоедством или чем? Код fasked'а вполне конкретно идентифицирует три разные ошибки пользователя: "Empty line", "Invalid character" и "Don't enter more than %d characters". А ты упорно пытаешься его сравнить с кодом, который кроме "Wrong input" ничего не выдаёт. fasked привязывается к ситуации в самом общем виде и демонстрирует постановку задачи на конкретном примере ввода double'а с более-менее внятной диагностикой ошибки, а ты кроме конкретной задачи ввода double'а никаких других задач не видишь и видеть не хочешь.
А в конечном итоге страдает пользователь. Он запускает программу, его просят ввести число. Он вводит "12,34". Программа ему говорит "пошёл на х..." (wrong input) и просит ввести заново. Пользователь говорит "сам пошёл на х...", закрывает программу и больше никогда к ней не вернётся. Пользователю и неведомо, что вместо запятой надо точку вводить. Он всю жизнь писал запятую, и не хочет из-за каких-то там идиотских стандартов вместо запятой рисовать точку. В варианте fasked'а хотя бы в первом приближении станет понятно, что тут не так
Ну а теперь расширенная постановка задачи: сделать так, чтобы при вводе плавающего числа понимался вариант как с запятой, так и с точкой. В варианте fasked'а это сделать довольно простоЗапись от Evg размещена 11.04.2012 в 15:52
-
Запись от -=ЮрА=- размещена 11.04.2012 в 18:47
-
Запись от Evg размещена 11.04.2012 в 18:50
-
Запись от Evg размещена 11.04.2012 в 18:51
-
Evg наша дискуссия меня уже немного начала дразнить, хочешь линукс, хочешь пустой ввод, хочешь мессаги от программы, да нет проблем
ОтработкаC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#include <stdio.h> #include <string.h> char * replComa(char *str) { char * chBuf = NULL; while(chBuf = strchr(str,',')) *chBuf = '.'; return str; } int main() { int ret; char inpStr[1024]; char bufStub = 0; double param = 0;//Вот важный момент начальная инициализация! while(1) { bufStub = 0; printf("Enter double : "); if((!scanf("%[^\n]%c",inpStr,&bufStub)) || bufStub != '\n') { if(printf("Empty input\n")) getchar(); } else if(308 < strlen(replComa(inpStr)))//Параллельно заменям все запятые на точки printf("Input is too large\n"); else if(!sscanf(inpStr,"%lf%c",¶m,&bufStub) || bufStub != '\n') printf("Errors in input(input contains alphas or has unsupported format)\n"); else printf("Your input %.11f\n",param);//Тоже мало кто знает что %.11f позволятет } return 0; }
Как видишь код абсолютно уже не уступает коду fasked хотя он короче и понятней новичку!C++ 1 2 3 4 5 6 7 8 9 10 11 12 13
Enter double : 123,123,123 Errors in input(input contains alphas or has unsupported format) Enter double : 12f Errors in input(input contains alphas or has unsupported format) Enter double : aaaa Errors in input(input contains alphas or has unsupported format) Enter double : 123,123 Your input 123.12300000000 Enter double : Empty input Enter double : 125. Your input 125.00000000000 Enter double :
Всё таки я думал ты прочёл что написал вначале
- Компилятор VS 6.0 NT platform. В 2008-й версии такого уже нет, а вот в старых версиях такой баг имеется. Я позволю сейчас не закачивать на файлообменник картинку краха, но если общественность будет настаивать то она появиться
Запись от -=ЮрА=- размещена 11.04.2012 в 21:32
-
Доп пояснение
Строка
не случайна, т.к. дабл имеет по максимум 308 знаков то вот и ограничим длинну вводимого. Не буду кривить душой я не такой мазохист чтобы вбивать 300 и более чаров, возможно к 308 нужно добавить 12 это уже на мой взгляд не суть важноC 1
if(308 < strlen(replComa(inpStr)))
- очень хорошая штука из API
FormatMessage http://www.vsokovikov.narod.ru... essage.htm
Не знаю возможно линукс обладает чем либо похожим
Дальнейшие вопросы и посты от общественности приветсвутся!
Запись от -=ЮрА=- размещена 11.04.2012 в 21:33
-
> Как видишь код абсолютно уже не уступает коду fasked хотя он короче и понятней новичку!
Только при этом содержит косяк - если вбить более 1024 символов в строку, то есть хороший шанс накрыть медным тазом правильную работу программы. Другими словами - отличная дыра для хакеров (именно через такие косяки проходит нехилая часть взломов системы). Хотя теперь программа уже ближе к тому состоянию, когда реально можно что-то сравнивать. Правда у fasked'а более точная диагностика на предмет недопустимого символа в вводе, а у тебя опять что-то непонятное и нечленораздельное, после которого обычный пользователь точно так же скажет "да пошли вы все на х..." и закроет программу. Т.е. "абсолютно не уступает" всё-таки далековато от истины
> на большинстве старых компиляторов приведёт к краху
Т.е. один несчастный компилятор под винду - это большинство?
> Я позволю сейчас не закачивать на файлообменник картинку краха, но если общественность будет настаивать то она появиться
Общественность в лице меня настаивает. Причём нужно закачать не только картинку краха, но и исходник. А так же подтверждение того, что автор ничего не намудрил с опциями компилятораЗапись от Evg размещена 11.04.2012 в 23:35
-
Evg с этим замечанием не соглашусь абсолютно, моя программа реагирует на путой ввод, некорректный ввод и по твоим просьбам ещё и запятые преобразует.
Есть желание получать подсказки в виде изложения на 5 страниц, просто вбей свои пояснения в данные ветви кода.
По мне кристально понятно
Пустая строка - Empty input
Слишком много ввели - Input is too large
Некорректный ввод - Errors in input(input contains alphas or has unsupported format)
Но хочешь напиши свои подсказки
- На счёт якобы дыры в коде, это настолько легко поправимо scanf("%1023[^\n], что честно читая твою ремарку невольно улыбнулся. Хорошо вот такой код уже и от "дыры" защищён
Хотя в принципе найти мазохиста вводящего вручную 1024 символа подряд довольно сложно, но гипотетически ситуации всякие бывают, хорошо замечание принял к сведенью.
Функционал аналогичен функционалу предыдущего кодаC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include <stdio.h> #include <string.h> char * replComa(char *str) { char * chBuf = NULL; while(chBuf = strchr(str,',')) *chBuf = '.'; return str; } int main() { char inpStr[1024]; char bufStub = 0; double param = 0;//Âîò âàæíûé ìîìåíò íà÷àëüíàÿ èíèöèàëèçàöèÿ! while(1) { bufStub = 0; printf("Enter double : "); if((!scanf("%1023[^\n]%c",inpStr,&bufStub)) || bufStub != '\n') { if(printf("Empty input\n")) getchar(); } else if(308 < strlen(replComa(inpStr)))//Ïàðàëëåëüíî çàìåíÿì âñå çàïÿòûå íà òî÷êè printf("Input is too large\n"); else if(!sscanf(inpStr,"%lf%c",¶m,&bufStub) || bufStub != '\n') printf("Errors in input(input contains alphas or has unsupported format)\n"); else printf("Your input %.11f\n",param);//Òîæå ìàëî êòî çíàåò ÷òî %.11f ïîçâîëÿòåò } return 0; }
Evg, далее сравни длинну кодов и посчитай эффективность моих 36-и строк и 79-ти у fasked. Только не надо говорить мол у него подсказки красивее, это уже на предвзятость смахивает
- я позволю себе ответить на данный вопрос в течении пары дней, как раз буду иметь возможность оттестировать абсолютно всё архаичное с датой выпуска 90-е годыЗапись от -=ЮрА=- размещена 12.04.2012 в 13:01
-
> Evg с этим замечанием не соглашусь абсолютно, моя программа реагирует на путой ввод, некорректный ввод
Твоя программа реагирует "чувак, ты где-то ошибся, но где конкретно - разбирайся сам". На этом в споре можно поставить точку. Подход к делу у fasked'а соответствует программному продукту, а твой подход - самопальной софтине для собственного использования. Т.е. пришли ровно к тому, с чего и начали - ты пытаешься сравнивать несравнимые вещи. Грубо говоря, ты сделал усилитель из одного транзистора и разглагольствуешь на тему, что HiEnd-системы - это 16-мильный крюк, потому что там необоснованно много транзисторов, хотя можно обойтись всего одним.
> На счёт якобы дыры в коде, это настолько легко поправимо scanf("%1023[^\n]
Но после этого останутся в буфере ввода залипшие данные. Т.е. очередной момент, когда ты куда-то спешишь и пишешь код тяп-ляп
> Только не надо говорить мол у него подсказки красивее
Опять ты нихрена не пытаешься понять, что тебе другие пишут. Именно ради этих красивых подсказок и затевался тот код на 100 строк, с которым ты постоянно сравниваешь свой код из 10 строк. И именно эти красивые подсказки отличают профессиональный программный продукт от самопальный софтин. И именно из-за подхода к работе, похожего на твой, и создаются дырявые программы, когда вместо того, чтобы один раз сделать основательно и по человечески, сначала делается так, чтоб было меньше букв, а при дальнейшей модернизации делаются необдуманные правки в коде, которые и создают дыру. fasked, к его чести, уже дорос до понимания того, что программу напишет каждый дурак, а нормальный пользовательский интерфейс к программе - тут уже надо думать и вылизывать. А ты, к сожалению, пока остаёшься на уровне домашних самоделокЗапись от Evg размещена 12.04.2012 в 14:57
-
- при правильнолм выборе рабочей точки и согласовании выходного трансформатора выходной каскад усилителя можно сделать с характеристиками близкими к ламповому, это по усилителю НЧ, а высокочастотные усилки предпочтительно паять из 1-го транзистора, т.к. каскадные усилителии вносят свой шум.
"Чувак я написал код короткий и понятный даже туркмен баши, хочешь писать длинно пиши ради бога..."
- напиши свои подсказки, алгоритм реагирует на некорректный ввод, точней на 3 его подвида - пустой ввод, некорректный формат и ввод с переполнением, надо подсказка на 6 листов сел вбил руками и вперёд в printf
Evg консоль это как говорит Зверев "...знаешь что с помидорами", Говоришь софтин: софтина должна иметь человеческий интерфейс(окно меню кнопки, элементы управления), а консольный интерфейс это читай дос. Ты считаешь что дос-подобные приложения это верх програмерского продукта?!
Да уж...
Запись от -=ЮрА=- размещена 12.04.2012 в 17:03
-
Запись от -=ЮрА=- размещена 12.04.2012 в 17:16





