TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: whdjr on April 24, 2006, 11:56:31 AM

Title: Testing a string for a number
Post by: whdjr on April 24, 2006, 11:56:31 AM
What would be a good and easy/quick way to test a string to see if it is a number?

(atoi "aaa")   ==> 0
(atoi "0")      ==> 0
(atoi "111")  ==> 111

Any ideas?
Title: Re: Testing a string for a number
Post by: CAB on April 24, 2006, 12:09:20 PM
How about
Code: [Select]
(numberp (read (substr str 1 1)))or
Code: [Select]
(numberp (read str))
Title: Re: Testing a string for a number
Post by: Alexander Rivilis on April 24, 2006, 12:11:18 PM
(distof "aaa")  ==> nil
(distof "0") ==> 0.0
(distof "111") ==> 111.0
Title: Re: Testing a string for a number
Post by: T.Willey on April 24, 2006, 12:13:26 PM
I think this might work.  Written on the fly.

Code: [Select]
(defun IsStrNums (String)

(and
 (mapcar
  '(lambda (x) (>= 48 x 57))
  (vl-string->list String)
 )
)
)

I think that is the right function to use (vl-string->list), but not sure.

Edit:  Change the test to greater than or equal, not less than or equal.
Title: Re: Testing a string for a number
Post by: CAB on April 24, 2006, 12:16:57 PM
That's a nice one Rivilis. :-)
Code: [Select]
(distof "2'-3 1/2\"")
27.5
Title: Re: Testing a string for a number
Post by: JohnK on April 24, 2006, 12:21:48 PM
Oho, potential fun: I think if i remeber correctly numbers take up codes 48 and up in ascii codes. ... 0 = 48 1 = 49...

Title: Re: Testing a string for a number
Post by: Alexander Rivilis on April 24, 2006, 12:33:34 PM
That's a nice one Rivilis. :-)
Code: [Select]
(distof "2'-3 1/2\"")
27.5
On my AutoCAD: (distof "2'-3 1/2\"") ==> nil because LUNITS is setting to 2
This function is dependent on LUNITS variable. If you need test only decimal number (not architectural, nor engineering)
you must use (distof val 2)  - val is a testing string.
Title: Re: Testing a string for a number
Post by: MP on April 24, 2006, 01:23:11 PM
On my AutoCAD: (distof "2'-3 1/2\"") ==> nil because LUNITS is setting to 2
This function is dependent on LUNITS variable. If you need test only decimal number (not architectural, nor engineering)
you must use (distof val 2)  - val is a testing string.

Perhaps this may be useful --

Code: [Select]
(defun MyDistOf ( string / result )
    (vl-some
       '(lambda (mode)
            (setq result
                (distof
                    string
                    mode
                )
            )
        )
       '(2 3 5 4 1)
    )
    result
)

From here (http://www.theswamp.org/index.php?topic=6799.msg83706#msg83706).
Title: Re: Testing a string for a number
Post by: whdjr on April 24, 2006, 01:24:07 PM
Thanks for the reply guys those are some great examples but I think for simplicity sake I will use the method suggested by CAB.

How about
...
Code: [Select]
(numberp (read str))

Thanks again guys,
Title: Re: Testing a string for a number
Post by: MP on April 24, 2006, 01:33:12 PM
I don't know the context you're going to use it Will but do be careful --

    Command: (numberp (read "123 abc")) => T

    Command: (numberp (mydistof "123 abc")) => nil

Should your host application consider "123 abc" a number?

Title: Re: Testing a string for a number
Post by: whdjr on April 24, 2006, 01:41:36 PM
Thanks Michael, 

The list will either be a number or a string not both so I think the numberp will work fine.  Thanks for your code though it definitely covers all the bases.  :-)
Title: Re: Testing a string for a number
Post by: whdjr on April 24, 2006, 01:51:12 PM
Michael,

Upon further trials:

    Command: (numberp (read "123 abc")) => T  -> In the VLIDE it strips everything off after the space.

    Command: (numberp (read "123abc")) => Nil  If you remove the space then it nil's out.

Just FYI.
 :-)
Title: Re: Testing a string for a number
Post by: MP on April 24, 2006, 01:58:25 PM
Precisely why I posted that example Will.

An aside, here's another variant I wrote that is relatively bomb proof --

Code: [Select]
(defun distanceof ( x  / result )
   (vl-catch-all-apply
      '(lambda ()
           (vl-some
              '(lambda (units) (setq result (distof x units)))
               (list (getvar "lunits") 4 5 2 1)
           )
       )
   )   
   result
)

From here (http://www.theswamp.org/index.php?topic=4376.msg52867#msg52867).

:)
Title: Re: Testing a string for a number
Post by: whdjr on April 24, 2006, 02:04:39 PM
Thanks Michael,

I like that one a little better because once it catches a T it stops processing.  Very COOL.

Thanks,
 :-)
Title: Re: Testing a string for a number
Post by: CAB on April 24, 2006, 02:33:39 PM
Thanks Michael..

Here is something to consider, although may not be relevant to Will.

Code: [Select]
Command: test1                             Command: test2                         
                                                                                   
*--> 3.5        gives number: 3.5          *--> 3.5        gives number: 3.5       
" type: " 2                                                                       
*--> 3 1/2      gives number: 3.5          *--> 3 1/2      gives number: 3.5       
" type: " 2                                                                       
*--> 3 1/2"     gives number: 3.5          *--> 3 1/2"     gives number: 3.5       
" type: " 3                                                                       
*--> 3 1/2'     gives number: 42.0         *--> 3 1/2'     gives number: 42.0     
" type: " 3                                                                       
*--> 3-1/2'     gives number: 42.0         *--> 3-1/2'     gives number: 42.0     
" type: " 3                                                                       
*--> 3'-1"      gives number: 37.0         *--> 3'-1"      gives number: 37.0     
" type: " 3                                                                       
*--> 3' -1"      gives number: 3             **  Invalid Number  **  3' -1"       
" type: " 6                                                                       
*--> 3'- 1"      gives number: 3             **  Invalid Number  **  3'- 1"       
" type: " 6                                                                       
*--> 3'-1       gives number: 37.0         *--> 3'-1       gives number: 37.0     
" type: " 3                                                                       
*--> 3' 1       gives number: 37.0         *--> 3' 1       gives number: 37.0     
" type: " 3                                                                       
*--> 3' 1"      gives number: 37.0         *--> 3' 1"      gives number: 37.0     
" type: " 3                                                                       
*--> 3' 1 1/2   gives number: 37.5         *--> 3' 1 1/2   gives number: 37.5     
" type: " 3                                                                       
*--> 3'-1-1/2   gives number: 37.5         *--> 3'-1-1/2   gives number: 37.5     
" type: " 3                                                                       
*--> 3' 1 1/2"  gives number: 37.5         *--> 3' 1 1/2"  gives number: 37.5     
" type: " 3                                                                       
*--> 3' 1.5     gives number: 37.5         *--> 3' 1.5     gives number: 37.5     
" type: " 3                                                                       
*--> 3' .5      gives number: 36.5         *--> 3' .5      gives number: 36.5     
" type: " 3                                                                       
*--> 3'.5      gives number: 36.5          *--> 3'.5      gives number: 36.5       
" type: " 3                                                                       
  **  Invalid Number  **  10-1               **  Invalid Number  **  10-1         
                                                                                   
*--> -10        gives number: -10.0        *--> -10        gives number: -10.0     
" type: " 2                                                                       
  **  Invalid Number  **  -10-1              **  Invalid Number  **  -10-1         
                                                                                   
*--> .5         gives number: 0.5          *--> .5         gives number: 0.5       
" type: " 2                                                                       
*--> 0.5        gives number: 0.5          *--> 0.5        gives number: 0.5       
" type: " 2                                                                       
*--> 5 -2       gives number: 5              **  Invalid Number  **  5 -2         
" type: " 6                                                                       
*--> 1/2        gives number: 0.5          *--> 1/2        gives number: 0.5       
" type: " 2                                                                       
*--> 1/2"       gives number: 0.5          *--> 1/2"       gives number: 0.5       
" type: " 3                                                                       
*--> 9 1/2      gives number: 9.5          *--> 9 1/2      gives number: 9.5       
" type: " 2                                                                       
*--> 1.55E+01   gives number: 15.5         *--> 1.55E+01   gives number: 15.5     
" type: " 2                                                                       
  **  Invalid Number  **  5A                 **  Invalid Number  **  5A           
                                                                                   
*--> 5 A        gives number: 5              **  Invalid Number  **  5 A           
" type: " 6




Code: [Select]
(defun c:test1 (/ txt txtlst num)
  (setq txtlst
         (list  "3.5       "   ; type:  2
                "3 1/2     "   ; type:  2 ?5
                "3 1/2\"    "  ; type:  4
                "3 1/2'    "   ; type:  4
                "3-1/2'    "   ; type:  4
                "3'-1\"     "  ; type:  4
                "3' -1\"     " ; Invalid Number
                "3'- 1\"     " ; Invalid Number
                "3'-1      "   ; type:  4
                "3' 1      "   ; type:  4
                "3' 1\"     "  ; type:  4
                "3' 1 1/2  "   ; type:  4
                "3'-1-1/2  "   ; type:  4
                "3' 1 1/2\" "  ; type:  4
                "3' 1.5    "   ; type:  4
                "3' .5     "   ; Invalid Number
                "3'.5     "    ; type   3
                "10-1      "   ; Invalid Number
                "-10       "   ; type:  2
                "-10-1     "   ; Invalid Number
                ".5        "   ; type:  2
                "0.5       "   ; type:  2
                "5 -2      "   ; type:  6
                "1/2       "   ; type:  2 ?5
                "1/2\"      "  ; type:  4
                "9 1/2     "   ; type:  2 ?5
                "1.55E+01  "   ; type:  2
                "5A        "   ; Invalid Number
                "5 A       "   ; type:  6
         )
  )
  (setq idx -1)

  ;;  distof will tollerate trailing spaces but not any other character
  ;;  therefore READ can be used to extract numbers followed by characters IF
  ;;  there is a space behing the number (read "12 A") = 12 (read "12a") = "12a"


  (while (< (setq idx (1+ idx)) (length txtlst))
    (setq txt (nth idx txtlst))
    ;;  must be tested in this order (2 1 5 4 3)
    ;; if you use (1 2 3 4 5) you will not get any 2 4 or 5 results
    ;;  further test result in inconsistancies with 3&4 and 5 types
    (cond ((setq num (distof txt 2))
           (setq typ 2)
          )
          ((setq num (distof txt 1))
           (setq typ 1)
          )
          ((setq num (distof txt 5))
           (setq typ 5)
          )
          ((setq num (distof txt 3))
           (setq typ 3)
          )
          ((setq num (distof txt 4))
           (setq typ 4)
          )
          ((setq num (read txt))
           (setq typ 6)
          ); get number if followed by a space "10 a" = 10
           ;; (read "10a") = "10a" so you must test result with
           ;;   (numberp "10a") results in 'not a number'
    )
    (if (numberp num) ;  Got a valid number
      (progn
        (prompt (strcat "\n*--> " txt " gives number: "))
        (princ num)
        (print " type: ")
        (princ typ)
      )
      (prompt (strcat "\n  **  Invalid Number  **  " txt))
    )
  )
  (princ)

)

test2
Code: [Select]
  (while (< (setq idx (1+ idx)) (length txtlst))
    (setq txt (nth idx txtlst))
   
    (setq num (distanceof txt))
    (if (numberp num) ;  Got a valid number
      (progn
        (prompt (strcat "\n*--> " txt " gives number: "))
        (princ num)
        (print)
      )
      (prompt (strcat "\n  **  Invalid Number  **  " txt))
    )
  )
Title: Re: Testing a string for a number
Post by: CAB on April 24, 2006, 02:38:02 PM
PS This routine came from a thread, maybe in the old swamp, not sure.
And I think Stig was the author.
Title: Re: Testing a string for a number
Post by: MP on April 24, 2006, 09:32:07 PM
Thanks Alan, sure wish Stig posted more often.

Here's the output from a quickly bashed out pile of code (sorry, it's pretty ugly) --

Code: [Select]
3.5       -> 3.5
3 1/2     -> 3.5
3 1/2"    -> 3.5
3 1/2'    -> 42.0
3-1/2'    -> 42.0
3'-1"     -> 37.0
3' -1"    -> nil
3'- 1"    -> nil
3'-1      -> 37.0
3' 1      -> 37.0
3' 1"     -> 37.0
3' 1 1/2  -> 37.5
3'-1-1/2  -> 37.5
3' 1 1/2" -> 37.5
3' 1.5    -> 37.5
3' .5     -> 36.5
3'.5      -> 36.5
10-1      -> nil
-10       -> -10.0
-10-1     -> nil
.5        -> 0.5
0.5       -> 0.5
5 -2      -> nil
1/2       -> 0.5
1/2"      -> 0.5
9 1/2     -> 9.5
1.55E+01  -> 15.5
5A        -> nil
5 A       -> nil

Code: [Select]
(   (lambda ( strings / maxlen )
        (setq maxlen
            (apply 'max
                (mapcar 'strlen strings)
            )
        )
        (mapcar
           '(lambda ( string / padding )
                (setq padding " ")               
                (princ
                    (strcat
                        (substr
                            (progn
                                (while
                                    (<
                                        (strlen
                                            (setq padding
                                                (strcat
                                                    padding
                                                    padding
                                                )
                                            )
                                        )
                                        maxlen
                                    )
                                )
                                (strcat string padding)
                            )
                            1
                            maxlen
                        )
                        " -> "
                        (   (lambda ( x / result )
                                (vl-catch-all-apply
                                   '(lambda ( )
                                        (vl-some
                                           '(lambda ( units )
                                                (setq result
                                                    (distof x units)
                                                )
                                            )
                                           '(4 5 2 1)
                                        )
                                    )
                                )
                                (vl-prin1-to-string result)
                            )
                            string
                        )
                        "\n"
                    )
                )   
            )
            strings
        )
        (princ)
    )
   '(
        "3.5"
        "3 1/2"
        "3 1/2\""
        "3 1/2'"
        "3-1/2'"
        "3'-1\""
        "3' -1\""
        "3'- 1\""
        "3'-1"
        "3' 1"
        "3' 1\""
        "3' 1 1/2"
        "3'-1-1/2"
        "3' 1 1/2\""
        "3' 1.5"
        "3' .5"
        "3'.5"
        "10-1"
        "-10"
        "-10-1"
        ".5"
        "0.5"
        "5 -2"
        "1/2"
        "1/2\""
        "9 1/2"
        "1.55E+01"
        "5A"
        "5 A"
    )
)
Title: Re: Testing a string for a number
Post by: CAB on April 25, 2006, 12:34:13 AM
Michael,
Your command of the language is astounding.
Title: Re: Testing a string for a number
Post by: MP on April 25, 2006, 01:15:57 AM
Thanks Alan, you are way too generous. I should have written it properly (i.e. read-able by others) but had about 5 minutes to knock it off. Normally I'd break a problem down to its constituent functions but I was real pressed for time; ergo the mess I posted. Don't have any more tine tonight either -- I'm struggling to post this as a sleeping pill slams my synapses; both of them.
Title: Re: Testing a string for a number
Post by: Didge on April 26, 2006, 06:10:41 AM
A very interesting thread Guys, I've often required a fail-safe string to number function, but something meaty enough to extract numbers from the following types of strings without falling over.

"Cable 1.2m deep"  and  "Cable depth 1.2m" 

To the user either string is much the same as the other, but ATOF tends to be a touch more fussy in it's requirements. I guess a character by character evaluation is called for.
Title: Re: Testing a string for a number
Post by: MP on April 26, 2006, 07:47:28 AM
Might be some tips you can glean in this (http://www.theswamp.org/index.php?topic=1419.0;all) thread.
Title: Re: Testing a string for a number
Post by: CAB on April 26, 2006, 11:22:07 AM
Here is another thread of intrest http://www.theswamp.org/index.php?topic=3710.0
Title: Re: Testing a string for a number
Post by: Didge on April 26, 2006, 12:25:19 PM
Many thanks for the links guys, consider that function well and truely in the bag :-)