Announcement

Collapse
No announcement yet.

Checking for numeric values in data entry

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Checking for numeric values in data entry

    I'm writing a program (my first in PB) in which the user will enter numbers, which may legitimately include zeroes, into a text box. Using examples given in PB documentation, the program checks that something has been entered using the len function, and uses the remove$ function to delete extraneous material, as below.

    CASE %IDOK
    IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
    CONTROL GET TEXT CBHNDL, %IDC_TEXTBOX1 TO Sentry
    IF LEN(TRIM$(Sentry)) = 0 THEN
    MSGBOX "You need to enter something", _
    %MB_OK OR %MB_TASKMODAL, "EnterNumber"
    CONTROL SET FOCUS CBHNDL, %IDC_TEXTBOX1
    ELSE
    y = VAL(REMOVE$(Sentry, "$, "))
    Sentry = STR$(y)
    MSGBOX "You entered" + Sentry, _
    %MB_OK OR %MB_TASKMODAL, "EnterNumber"
    CONTROL SET FOCUS CBHNDL, %IDC_TEXTBOX1
    DIALOG END CBHNDL, %IDOK
    END IF
    END IF

    My question concerns the checking of whether the entry is a valid number. If the user has entered a letter (say), the val function returns a zero. However, zero is also legitimate value. So, my problem is how to distinguish the two.

    As I'm sure that many of you will have faced this question and solved it, I thought I'd ask, rather than continue to labour.

    Thanks for any help you can give me.

    Alan

    #2
    why not just add the %ES_NUMBER style to the CONTROL ADD TEXTBOX statement, that way only 0..9 may be entered.

    Or check that the ASC value of the character is in the numeric range.

    Comment


      #3
      One approach is
      Code:
      IF TRIM$(user_input_string) = "0" THEN
          do_something
      ELSE
          IF VAL(user_input_string) = 0 THEN ? "not a number"
      END IF
      If 0 will be used often, you could make radio buttons
      [o] 0 [o] Other value: [_____]
      Then use VAL against the Other value when 0 is not chosen.

      If you have a limited number of acceptable numbers, consider a COMBOBOX instead.

      Do you need to account for negative numbers, scientific notation, and/or decimals? If so, you will obviously need a more complex checking routine.
      Erich Schulman (KT4VOL/KTN4CA)
      Go Big Orange

      Comment


        #4
        Thanks

        Thanks Chris and Erich, for your helpful replies. I should have mentioned that this is the front end of a small statistics routine, in which users will be entering fractional numbers, sometimes negative. I guess (but haven't checked) the %ES_NUMBER handles only integers, and would baulk at decimal points and minus signs, Chris.

        Yes, thanks, Chris, I was thinking about the possibility of checking for a range of ASCII codes for numbers, decimal points and negative signs, and you confirmed that as a possibility.

        I think your code would do the trick, Erich. In the first line

        IF TRIM$(user_input_string) = "0" THEN

        I only have to worry about 0, and possibly 0.0 and possibly -0 or -.0.0 (if the number, say a mean, was very small, and the source of the data being entered only gave a few zeroes.

        I'll check out COMBOBOXs.

        Thanks again.

        As an aside, one of the daunting things about PowerBASIC, or any other Windows programming language, I suppose (as opposed to DOS QuickBASIC, which I've used for years) is the number of things external to PB that you have to learn, such as the Windows codes and messages, such as %WM_NCACTIVATE, etc, etc. QB is very self-contained and took care of some things without fuss. For example I wouldn't have to worry about the present issue in QB; if I'd used

        INPUT "Enter a number"; y

        and the user entered a, QB would just say

        redo from start

        and represent the prompt.

        My users are less and less receptive to non-GUI programs, though. I'll just have to get used to writing sub-routines to Windows.

        Thanks

        Alan

        Comment


          #5
          Coincidentally something similar is being discussed in another thread..http://www.powerbasic.com/support/pb...ad.php?t=22185

          There, MCM has pointed out the value of PB's Verify function..
          Code:
          #DIM ALL
          #INCLUDE "WIN32API.INC"
          FUNCTION PBMAIN()
           DIM sTest$, sMsg$, x&
           
            sTest = "-0.000z0345"                    ' remove z and recompile
            x = VERIFY (sTest , "-.1234567890")
            IF x > 0 THEN
              sMsg = "Illegal Char " +"("+ MID$(sTest, x, 1)+") at: " + STR$(x)
            ELSE
              sMsg = sTest + " is legal!"
            END IF
            MSGBOX sMsg,,"Result"
           
          END FUNCTION
          '------------------/
          P.S. See code tags here Here
          Rgds, Dave

          Comment


            #6
            Of course, even with QuickBasic you could use Line Input and not have to
            worry about Redo from start. You would just have to remove the comma before evaluating. The same (line input) is available in PBcc. With the Remove$ statement it's easy to remove anything not wanted.
            Client Writeup for the CPA

            buffs.proboards2.com

            Links Page

            Comment


              #7
              Or you could localize all the rules in a superclass of the edit control, or you could scrap the edit control altogether and subclass a static control to catch each character and handle it like a DOS application would, rebuilding and re-displaying the string in response to each keystroke. It would be easy to add rules to this to suit the type of data input required, even to count the keystrokes (some clerks - lucky souls - are paid per keypress or have to make at least n keypresses per hour or day).

              Comment


                #8
                Rules can be written for a customized input routine

                Code:
                #COMPILE EXE
                #DIM ALL
                FUNCTION PBMAIN () AS LONG
                  LOCAL s AS STRING
                  s = "993.99" 'valid
                  s = "-1-"    'invalid
                  s = "."      'invalid
                  s = ".9"     'valid
                  s = ".9."    'invalid
                  s = ".9.9"   'invalid
                  s = ".3H45"  'invalid
                  s = "5."     'invalid
                  s = "1,000"  'invalid
                  IF IsNumeric(s) THEN ? "Valid" ELSE ? "Invalid"
                  SLEEP 3000  'if using PBCC see results
                END FUNCTION
                FUNCTION IsNumeric(Answer AS STRING) AS LONG
                  'Rule 1   Something must have been entered
                  'Rule 2   .-0123456789 are the only valid characters
                  'Rule 3   . may not be the last character
                  'Rule 4   - can only be first character
                  'Rule 5   . only valid once
                  '? statements left in for testing purposes
                 
                  LOCAL Valid_Characters AS STRING
                  LOCAL s                AS STRING
                  LOCAL Position         AS LONG
                  Valid_Characters = ".-0123456789"
                 
                  IF LEN(Answer) = 0 THEN EXIT FUNCTION      'Rule 1 failed no length
                 
                  FOR Position = 1 TO LEN(Answer)            'Rule 2 each character must be valid
                    s = MID$(Answer,Position,1)
                    IF INSTR(Valid_Characters,s) = 0 THEN
                      ? $DQ + s + $DQ " is an invalid character"
                      EXIT FUNCTION
                    END IF
                  NEXT
                 
                  IF RIGHT$(Answer,1) = "." THEN             'Rule 3 Last character can't be a "."
                    ? ". Invalid in last position"
                    EXIT FUNCTION
                  END IF
                  Position = INSTR(-1,Answer,"-")
                  IF Position > 1 THEN                       'Rule 4 - may only be in the first position
                    ? "- not allowed in position" + STR$(Position)
                    EXIT FUNCTION
                  END IF
                 
                   Position = TALLY(Answer,".")
                   IF Position > 1 THEN
                     ? "Multiple decimal points"
                     EXIT FUNCTION
                   END IF
                 
                  ? "Passed all rules"
                  FUNCTION = 1                               'Passed all rules, set to TRUE
                END FUNCTION

                Comment


                  #9
                  Thanks again

                  Thanks Dave, Fred and Chris,
                  All very helpful and informative. Yes, I think I know what you mean Chris, with the character-by-character snatching of each keystroke. But maybe that's more than I need.

                  I'm trying the strategy which you drew my attention to, Dave, using the VERIFY function:

                  i& = VERIFY(Sentry, "-+.0123456789")

                  I'll take the risk that someone enters something awful, like 2.4-.45, for example.

                  Thanks

                  Alan

                  Comment


                    #10
                    Risk is not an option using post #8.

                    Comment


                      #11
                      Same thing without comments.

                      Code:
                      FUNCTION PBMAIN () AS LONG
                        IF IsNumeric("2.4-.45") THEN ? "Valid" ELSE ? "Invalid"
                        SLEEP 3000
                      END FUNCTION
                       
                      FUNCTION IsNumeric(Answer AS STRING) AS LONG
                        LOCAL Valid_Characters$,s$,Position&
                        Valid_Characters = ".-0123456789"
                        IF LEN(Answer) = 0 THEN EXIT FUNCTION
                        FOR Position = 1 TO LEN(Answer)
                          s = MID$(Answer,Position,1)
                          IF INSTR(Valid_Characters,s) = 0 THEN EXIT FUNCTION
                        NEXT
                        IF RIGHT$(Answer,1) = "." THEN EXIT FUNCTION
                        IF INSTR(-1,Answer,"-") > 1 THEN EXIT FUNCTION
                        Position = TALLY(Answer,".")
                        IF Position > 1 THEN EXIT FUNCTION
                        FUNCTION = 1
                      END FUNCTION

                      Comment


                        #12
                        Code:
                        FUNCTION PBMAIN () AS LONG
                          IF IsNumeric("2.4A-.45") THEN ? "Valid" ELSE ? "Invalid"
                        END FUNCTION
                         
                        FUNCTION IsNumeric(Answer AS STRING) AS LONG
                          IF LEN(Answer) = 0                  THEN EXIT FUNCTION
                          IF VERIFY (Answer , ".-0123456789") THEN EXIT FUNCTION
                          IF RIGHT$(Answer,1) = "."           THEN EXIT FUNCTION
                          IF INSTR(-1,Answer,"-") > 1         THEN EXIT FUNCTION
                          IF TALLY(Answer,".") > 1            THEN EXIT FUNCTION
                          FUNCTION = 1
                        END FUNCTION

                        Comment


                          #13
                          Check if an input is a number

                          I developed the following program to check if a string can be considered a number. Use it if you can found it useful.

                          Code:
                          FUNCTION CheckNum&(Stringa$) EXPORT
                              ' - F. Danuso (14/3/2002)
                              ' - Ccheck if a string can be considered (treated as) a number
                              ' - Return 1 if number, 0 if not.
                              LOCAL I&,A$,C$
                              IF TRIM$(Stringa$)="" THEN CheckNum&=0 : EXIT FUNCTION
                              A$=LCASE$(TRIM$(Stringa$))              '  0.25D-12
                              REPLACE "d" WITH "e" IN A$              '  0.25e-12  (normalize to e)
                          
                              ' ===== check first character ===========
                              I&=1
                              C$=MID$(A$,I&,1)
                              IF INSTR("0123456789+-.",C$)=0 THEN CheckNum&=0 : EXIT FUNCTION
                          
                              ' ===== check characters from second up to the end or 'e' ============
                              INCR I&
                              C$=MID$(A$,I&,1)
                              WHILE INSTR("0123456789.",C$)>0 AND I&<=LEN(A$)
                                  INCR I&
                                  C$=MID$(A$,I&,1)
                              WEND
                              IF I&>LEN(A$) THEN
                                  CheckNum&=1 : EXIT FUNCTION     ' true number
                              ELSE
                                  ' here a character different from a digit
                                  IF C$="e" THEN
                                      INCR I&
                                      C$=MID$(A$,I&,1)   ' get a character after 'e'
                                      IF INSTR("0123456789+-.",C$)=0 THEN CheckNum&=0 : EXIT FUNCTION
                                      INCR I&
                                      C$=MID$(A$,I&,1)
                                      WHILE INSTR("0123456789",C$)>0 AND I&<=LEN(A$)  ' this time, not . + -
                                          INCR I&
                                          C$=MID$(A$,I&,1)
                                      WEND
                                      IF I&>LEN(A$) THEN CheckNum&=1     ' true number
                                  ELSE
                                      CheckNum&=0 : EXIT FUNCTION
                                  END IF
                              END IF
                           END FUNCTION
                          Regards

                          Francesco Danuso
                          Last edited by Francesco Danuso; 8 Jan 2009, 03:05 AM.

                          Comment


                            #14
                            How about 1 line. Macro anyone?

                            Code:
                            FUNCTION PBMAIN () AS LONG
                             
                               Caption$ = "Numeric Check"
                               Prompt$  = "Please enter a number"
                             
                               DO
                                Answer$ = INPUTBOX$(Prompt$,Caption$,Answer$)
                                IF LEN(Answer$) = 0 THEN EXIT DO
                             
                                IF ( LEN(Answer$) = 0 )                  OR _
                                   ( VERIFY (Answer$ , ".-0123456789") ) OR _
                                   ( RIGHT$(Answer$,1) = "." )           OR _
                                   ( RIGHT$(Answer$,1) = "-" )           OR _
                                   ( INSTR(-1,Answer$,"-") > 1 )         OR _
                                   ( TALLY(Answer$,".") > 1 )         THEN  _
                                Prompt$ = "Invalid" ELSE Prompt$ = "Valid"
                             
                              LOOP WHILE LEN(Answer$)
                            END FUNCTION

                            Code:
                            FUNCTION IsNumber(sNumber AS STRING) AS LONG
                             
                              IF ( LEN(sNumber) = 0 )                  OR _
                                 ( VERIFY (sNumber , ".-0123456789") ) OR _
                                 ( RIGHT$(sNumber,1) = "." )           OR _
                                 ( RIGHT$(sNumber,1) = "-" )           OR _
                                 ( INSTR(-1,sNumber,"-") > 1 )         OR _
                                 ( TALLY(sNumber,".") > 1 )         THEN EXIT FUNCTION
                             
                              FUNCTION = 1 'return TRUE if valid number
                            END FUNCTION
                            Last edited by Mike Doty; 8 Jan 2009, 04:16 AM. Reason: Improved function to catch a single key of "-"

                            Comment


                              #15
                              Wow

                              Golly, thanks so much, Mike and Francesco. Your two sets of code are an education as well as a solution. I'm going to take some time to digest them, and then get back to you.

                              Alan

                              Comment


                                #16
                                Code:
                                'This routine is not optimized. It calls 6 functions.
                                'Someone may show a better STRPTR or ASM solution.
                                '
                                'If you stick with PowerBASIC calls your code will
                                'recompile with newer compiler versions.  If you use
                                'ASM or API calls it may need updating in the future.
                                '
                                #COMPILE EXE
                                #DIM ALL
                                DECLARE FUNCTION IsNumber(sNumber AS STRING) AS LONG
                                 
                                FUNCTION PBMAIN () AS LONG
                                  LOCAL Result AS LONG
                                  Result = IsNumber("123--2")  '1=true, 0=false
                                  ? STR$(Result)
                                  SLEEP 1000 'for PBCC to see results
                                END FUNCTION
                                FUNCTION IsNumber(sNumber AS STRING) AS LONG
                                  IF ( LEN(sNumber) = 0 )                  OR _
                                     ( VERIFY (sNumber , ".-0123456789") ) OR _
                                     ( RIGHT$(sNumber,1) = "." )           OR _
                                     ( RIGHT$(sNumber,1) = "-" )           OR _
                                     ( INSTR(-1,sNumber,"-") > 1 )         OR _
                                     ( TALLY(sNumber,".") > 1 )         THEN EXIT FUNCTION
                                  FUNCTION = 1 'return TRUE if valid number
                                END FUNCTION

                                Comment


                                  #17
                                  Me, too:
                                  Code:
                                    i& = VERIFY(Sentry, "-+.0123456789") 
                                    iF ISFALSE I& THEN         Sentry contains only valid characters 
                                       IF VAL(FORMAT$(VAL(Sentry)) <> VAL(Sentry) THEN 
                                          MSGBOX "String contains only valid characters," _
                                                 & " but something awful was entered!"
                                  
                                  
                                    ...
                                  Michael Mattias
                                  Tal Systems (retired)
                                  Port Washington WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                    #18
                                    Code:
                                    REM IF VAL(FORMAT$(VAL(Sentry))) <> VAL(Sentry) THEN  'always false
                                    REM Sniplets are fine, but code with a PBMAIN would have caught this
                                    '*******************************************************
                                    #DIM ALL
                                    FUNCTION PBMAIN () AS LONG
                                       LOCAL Caption$, Prompt$,Sentry$, i&
                                       Caption$ = "Numeric Check"
                                       Prompt$  = "Please enter a number"
                                       sEntry$  = "1..3"
                                       DO
                                        sEntry$ = INPUTBOX$(Prompt,Caption,sEntry)
                                        IF LEN(sEntry) = 0 THEN EXIT DO
                                        REM IF IsNumber(sEntry) Then ? "Valid" ELSE ? "InValid"
                                    '----------------  TESTING THIS  -----------------------------
                                        i& = VERIFY(Sentry, "-+.0123456789")
                                        IF ISFALSE I& THEN
                                         IF VAL(FORMAT$(VAL(Sentry))) <> VAL(Sentry) THEN  'always false
                                            ?  "String contains only valid characters," _
                                                   & " but something awful was entered!"
                                         ELSE
                                            ? sEntry  + " contains only valid characters (added this)"
                                         END IF
                                        ELSE
                                            ? "VERIFY FAILED"
                                        END IF
                                      LOOP WHILE LEN(Sentry)
                                    END FUNCTION
                                    FUNCTION IsNumber(sNumber AS STRING) AS LONG
                                      IF ( LEN(sNumber) = 0 )                  OR _
                                         ( VERIFY (sNumber , ".-0123456789") ) OR _
                                         ( RIGHT$(sNumber,1) = "." )           OR _
                                         ( RIGHT$(sNumber,1) = "-" )           OR _
                                         ( INSTR(-1,sNumber,"-") > 1 )         OR _
                                         ( TALLY(sNumber,".") > 1 )         THEN EXIT FUNCTION
                                      FUNCTION = 1 'return TRUE if valid number
                                    END FUNCTION
                                    Last edited by Mike Doty; 8 Jan 2009, 01:39 PM.

                                    Comment


                                      #19
                                      ASM specialized VAL statement

                                      This is related, pulls any valid character out of a string.
                                      This is a specialized VAL statement.
                                      If user types garbage, this disregards it so it may be inappropriate.
                                      Since it uses ASM, it may have to be updated in the future!
                                      A look at some ASM.

                                      This code is useful for placing strings into LONG's and displaying LONGS as money.
                                      LONGS can be thought of as the number of pennies. Saves storage and is very fast.
                                      The code is faster than VAL without truncating characters.
                                      It does not have the features of VAL, it is limited to a small character set.

                                      Code:
                                      DECLARE FUNCTION String2Long (str AS STRING) AS LONG
                                      DECLARE FUNCTION String2Money(str AS STRING) AS STRING
                                      FUNCTION string2money(str AS STRING) AS STRING
                                        FUNCTION =  USING$(",.##",string2long(str) *.01)
                                      END FUNCTION
                                       
                                      FUNCTION PBMAIN () AS LONG
                                         sEntry$  = "Power 1 BASIC, 2 is 3 great! 4 - it!"
                                          sEntry$ = INPUTBOX$("Enter anything","ASM String2Long",sEntry$)
                                          result& = String2Long(sEntry$)
                                          ? "String2Long:    "   + STR$(result&)                + $CRLF + _
                                            "USING:            " + USING$(",.##",result& * .01) + $CRLF + _
                                            "String2Money:"      + String2Money(sEntry$)
                                      END FUNCTION
                                      'use "012345679-" at any position to create long value
                                      'does not terminate on invalid characters
                                      'credits to:  john gleason,  paul dixon,  mike trader, mike doty (me)
                                      'http://www.powerbasic.com/support/pbforums/showthread.php?t=14373i]
                                      '--------------------------------------------------------------------------------------------
                                      '3/29/08 Correction:  These 2 lines were somehow missing in previous posting and the code GPF'ed
                                      FUNCTION string2long (sNum AS STRING) AS LONG
                                      !mov edi,sNum                   ;get the pointer to the string information (not the string)
                                      !mov edi,[edi]                  ;get the point to the string contents  (added this)
                                      '--------------------------------------------------------------------------------------------
                                      !xor edx,edx                    ;the sum =0
                                      !xor ecx,ecx                    ;the neg flag =0
                                      !movzx eax,byte ptr [edi]       ;get the first character
                                      lp:
                                      !cmp eax,"0"                    ;is character < a "0"?
                                      !jl  lessthan0                  ;yes, skip to the non-digit check
                                      !cmp eax,"9"                    ;is it greater than a "9"?
                                      !jg  nextcharacter              ;yes, get next char.
                                      'to get here it must be a digit
                                      !imul edx,10                    ;sum=sum*10 ..
                                      !add edx,eax                    ;    + digit
                                      !sub edx,48                     ;    - 48
                                      !jmp nextcharacter
                                      lessthan0:
                                      REM !cmp eax,"."                ;is it a "."           'rem to terminate on decimal
                                      REM !je done                    ;yes, then exit loop   'rem to terminate on decimal
                                      !cmp eax,"-"                    ;is it a "-"
                                      !jne nextcharacter              ;no, get next character
                                      !mov ecx,1                      ;set the neg flag
                                      nextcharacter:
                                      !inc edi                        ;increment the string pointer
                                      !movzx eax,byte ptr [edi]       ;get next character
                                      !or eax,eax                     ;test if zero
                                      !jnz lp                         ;not zero, go back and do next character
                                      done:
                                      !cmp ecx,0                      ;is neg flag set?
                                      !je skip                        ;no, skip next instruction
                                      !neg edx                        ;yes, negate answer
                                      skip:
                                      !mov function,edx               ;write answer to function
                                      END FUNCTION
                                       
                                      ' [URL]http://www.powerbasic.com/support/pbforums/showthread.php?t=25163[/URL]
                                      'string2long.bas
                                      '
                                      'purpose:  1) extract "0123456789-" from a string to create a long.
                                      '          2) val terminates on invalid characters.  this does not.
                                      '          3) eliminate need for a string function to extract result.
                                      '          4) fastest performance for this specific purpose.
                                      '          5) suggestion for "optimized val for each data type" officially rejected by pb.
                                                    'val supports:
                                                    'scientific notation
                                                    'rounding of floating point values assigned to an integer
                                                    'radix
                                                    'unary operators
                                                    'overflow
                                      '
                                      'comments:    'add-on library, include file or dll containing assembler routines
                                      '             'would make an excellent addition for ultimate performance.
                                      '
                                      'modified: 7/31/07 only updated the documention
                                      Last edited by Mike Doty; 8 Jan 2009, 01:49 PM.

                                      Comment

                                      Working...
                                      X
                                      😀
                                      🥰
                                      🤢
                                      😎
                                      😡
                                      👍
                                      👎