Тема: Как проанализировать что пользователь в DCL-окне ввел целое число?
Как поанализировать на случай ошибки, что пользователь ввел целое число в Edit Box DCL окна?
Там же все значения строковые...
atoi преобразует строки в целое - 0
Информационный портал для профессионалов в области САПР
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Форумы CADUser → Программирование → DCL → Как проанализировать что пользователь в DCL-окне ввел целое число?
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Как поанализировать на случай ошибки, что пользователь ввел целое число в Edit Box DCL окна?
Там же все значения строковые...
atoi преобразует строки в целое - 0
все зависит от конкретной задачи, но на вскидку есть два варианта:
1. сперва добавляешь к строке скобки (круглые) спереди открывающую, сзади - закрывающую; теперь запускаем полученную строку в read и анализируеш тип всех элементов полученного списка - для текста получится SYM, для чисел - REAL или INT.
2. можно попробовать анилизировать строку при помощи WCMATCH - надо только с шаблоном повозиться...
Может, проверить строку на наличие точки или запятой как десятичного разделителя?
Я делаю примерно так:
1. (atoi ...
2. Если возвращает 0 (юзер ввел буквы вместо числа),
то что-то типа (alert "А ну введи целое число!") и потов возврат снова в то же диалоговое окно.
Проверка строки на соответствие целому числу:
(defun checkInt (str:Int / str:Test)
(setq str:Test "") ; инициализация строки-шаблона
(eval (list 'wcmatch
str:Int
(repeat (strlen str:Int) ;формирование строки-шаблона
(setq str:Test (strcat str:Test "#"))
) ;_ repeat
) ;_ list
) ;_ eval
) ;_ defun
Вызов: (checkInt "12345678") возвращает T
(checkInt "abcdef") возвращает nil
Удачи.
> kos
(11.04.2003 в 14:32:15)
Справедливо только для чисел >= 0. Кроме того целое число лежит в диапазоне от +2,147,483,647 до ?2,147,483,648. Все что больше или меньше сводится к ближайшему пределу. Например, (atoi "2147483648") вернет 2147483647 (на еденицу меньше). А функция (checkInt) при этом вернет t.
(defun checkInt2 (str:Int / answer)
(if (equal (type (setq answer (read str:Int))) 'INT)
answer
);if
) ;_ defun
Возвращет число или nil.
Да, про отрицательные числа упустил... Поэтому предлагаю немного измененный вариант - инициализация строки-шаблона должна выглядеть так:
(setq str:Test "[-0123456789]")
По поводу диапазона целых чисел: эта функция только проверяет строку посимвольно на наличие "чужеродных" (не цифра) символов ничего при этом не преобразовывая.
> "Да,
про отрицательные числа упустил... Поэтому предлагаю немного измененный вариант - инициализация строки-шаблона должна выглядеть так:
(setq str:Test "[-0123456789]")" <
В этом случае "---45---64" тоже вернет t.
> "По поводу диапазона целых чисел: эта функция только проверяет строку посимвольно на наличие "чужеродных" (не цифра) символов ничего при этом не преобразовывая." <
Так это-то и плохо, вопрос ведь был про Edit Box. И предполагается, если функция вернет t, сразу переводить ее в число. А если даипазон превышен, то получится неверный результат.
1.
Неправда ваша: "---45---64" вернет nil.
Инициализация строки-шаблона задает только первый символ шаблона, полностью он формируется добавлением к нему символа "#" в функции (repeat). Кстати, после первого исправления функции забыл добавить:
(repeat (1- (strlen str:Int) ) и далее по тексту...
2.
Да, согласен. Я просто предложил реализацию идеи Сергея Попадьина (п.2).
По поводу сообщения в пятницу kos (11.04.2003 в 16:40:00), я решил, что строка
(setq str:Test "[-0123456789]")
просто опечатка, а на самом деле имелось в виду
(setq str:Test (strcat str:Test "[-0123456789]"))
а все остальное без изменений.
Но, если я правильно понял сообщение kos (14.04.2003 в 09:34:35) п.1. Функция должна выглядеть так:
(defun checkInt3 (str:Int / str:Test)
(setq str:Test "[-0123456789]") ; инициализация строки-шаблона
(eval (list 'wcmatch
str:Int
(repeat (1- (strlen str:Int)) ;формирование строки-шаблона
(setq str:Test (strcat str:Test "#"))
) ;_ repeat
) ;_ list
) ;_ eval
) ;_ defun
Но это еще хуже - после применения
(checkInt3 "1")
получим
error: bad argument type: stringp nil
Уж если делать, через wcmatch, то примерно так
(defun CheckInt4 (string / template lng)
(if (> (setq lng (strlen string)) 1)
(progn
(setq template "[-+0123456789]")
(repeat (1- lng)
(setq template (strcat template "#"))
);repeat
);progn
(setq template "#")
);if
(wcmatch string template)
);defun
А если продолжить идею через (read), то можно обойтись одной функцией и для целых чисел, и для действительных
(defun is_it_number (string type_nmb / answer)
(cond
((equal type_nmb 'INT)
(if (equal (type (setq answer (vl-catch-all-apply 'read (list string)))) type_nmb)
answer
);if
)
((equal type_nmb 'REAL)
(if (or
(equal (type (vl-catch-all-apply 'read (list string))) type_nmb)
(is_it_number string 'INT)
);or
(atof string)
);if
)
);cond
);defun
Т.е. для проверки целого числа
(is_it_number "345" 'INT)
а для действительного
(is_it_number "345" 'REAL)
Ну а можно окончательно обрадовать пользователя, и позволить ему прямо в edit_box производить какие-то элементарные вычисления.
;*********************************************************************************
; Функция phrase_to_words
;
; Переделывает фразу в список слов в соответствии со списком разделителей.
; Если param=t, то разделители включаются в список слов, иначе не включаются.
; Возвращает список слов.
;*********************************************************************************
(defun phrase_to_words (string lst_divider param / lst_answer strmus strcus)
(setq lst_answer '() strcus "")
(repeat (strlen string)
(if (member (setq strmus (substr string 1 1)) lst_divider)
(progn
(if (/= strcus "")
(setq lst_answer (cons strcus lst_answer))
);if
(if param
(setq lst_answer (cons strmus lst_answer))
);if
(setq strcus "")
);progn
(setq strcus (strcat strcus strmus))
);if
(setq string (substr string 2))
);repeat
(if (/= strcus "")
(setq lst_answer (cons strcus lst_answer))
);if
(if lst_answer
(reverse lst_answer)
'("")
);if
);defun
;*********************************************************************************
; Функция mini_calc
;
; Возвращает или само число, или результат последовательных вычислений, или nil.
;*********************************************************************************
(defun mini_calc (string / str_work answer lst_action lst_word lng_word nmb
action value)
(setq str_work (vl-list->string
(subst (ascii ".") (ascii ",")
(vl-string->list (vl-string-trim " " string)))))
(if (setq answer (is_it_number str_work 'REAL))
answer
(progn
(setq lst_action (list
(list "+" '+)
(list "-" '-)
(list "*" '*)
(list "/" '/)
(list "^" 'expt)
));setq
(setq lst_word (phrase_to_words str_work (mapcar 'car lst_action) t))
(if (or
(= (car lst_word) "-")
(= (car lst_word) "+")
);or
(setq lst_word (cons "0" lst_word))
);if
(if (and
(> (setq lng_word (length lst_word)) 2)
(/= (/ lng_word 2) (/ lng_word 2.0))
(setq answer (is_it_number (car lst_word) 'REAL))
);and
(progn
(setq nmb 1)
(while (< nmb (1- lng_word))
(if (and
(setq action (cadr (assoc (nth nmb lst_word) lst_action)))
(setq value (is_it_number (nth (1+ nmb) lst_word) 'REAL))
);and
(if (vl-catch-all-error-p (setq answer (vl-catch-all-apply action (list answer value))))
(setq answer nil nmb lng_word)
(setq nmb (+ nmb 2))
);if
(setq answer nil nmb lng_word)
);if
);while
answer
);progn
);if
);progn
);if
);defun
Например, (mini_calc "20-10+5/3*5^0.5") вернет 5.0. Причем при вводе строки в качестве разделителя целой и дробной части числа можно использовать как ".", так и ",".
Принимаю все замечания, но прошу учесть, что пример клепался на колене в течение 2-х минут и никак не тестировался и не отлаживался. Поэтому отсутствие ошибок я расценил бы как фантастику.
Спасибо за полезные функции.
В поставку AutoCAD входит файл ai_utils.lst очень полезный для изучения.
Собственно оттуда:
;;;
;;; Pass a number, an error message, and a range. If the value is good, it is
;;; returned, else an error is displayed.
;;; Range values:
;;; 0 - any numeric input OK
;;; 1 - reject positive
;;; 2 - reject negative
;;; 4 - reject zero
;;;
(defun ai_num (value error_msg range / good_value)
(cond
;; is it a number
((not (setq good_value (distof value)))
(set_tile "error" error_msg)
nil
)
;; is it positive
((and (= 1 (logand 1 range))
(= (abs good_value) good_value)
)
(set_tile "error" error_msg)
nil
)
;; is it zero
((and (= 2 (logand 2 range))
(= 0.0 good_value)
)
(set_tile "error" error_msg)
nil
)
;; is it negative
((and (= 4 (logand 4 range))
(/= (abs good_value) good_value)
)
(set_tile "error" error_msg)
nil
)
(T good_value)
)
)
Возвращает либо вещественное число, либо nil.
Дальше анализируем вешественное число (что у него после запятой) примерно так:
(if (< (- <real> (fix <real>)) 0)
(введено не целое)
(введено целое)
)
Поправка:
(if (> (- <real> (fix <real>)) 0)
(введено не целое)
(введено целое)
)
> Andrew
1. Если не менять саму функцию, то хотя бы изменить ее описание:
;;; Range values:
;;; 0 - any numeric input OK
;;; 1 - reject positive
;;; 4 - reject negative
;;; 2 - reject zero
2. Для целых чисел не решет проблемы с диапазоном (см. leha (11.04.2003 в 15:40:56) )
3. Уже не так важно, но все-таки: 0 отнесли к положительным числам.
1. Функция не моя и просто скопирована без изменений. Сам использовал (и без изменений, и с модификациями) - очень удобно.
Это к тому что во многих случаях нет смысла изобретать велосипед.
2. А это вообщето не проблема, это именно допустимый диапазон целых чисел. И если планируются бОльшие числа, то рациональнее, по моему, работать с вещественными.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Форумы CADUser → Программирование → DCL → Как проанализировать что пользователь в DCL-окне ввел целое число?
Форум работает на PunBB, при поддержке Informer Technologies, Inc