Announcement

Collapse
No announcement yet.

Checking for numeric values in data entry

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

  • Mike Doty
    replied
    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.

    Leave a comment:


  • Mike Doty
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    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!"
    
    
      ...

    Leave a comment:


  • Mike Doty
    replied
    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

    Leave a comment:


  • Alan James Taylor
    replied
    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

    Leave a comment:


  • Mike Doty
    replied
    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 "-"

    Leave a comment:


  • Francesco Danuso
    replied
    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.

    Leave a comment:


  • Mike Doty
    replied
    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

    Leave a comment:


  • Mike Doty
    replied
    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

    Leave a comment:


  • Mike Doty
    replied
    Risk is not an option using post #8.

    Leave a comment:


  • Alan James Taylor
    replied
    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

    Leave a comment:


  • Mike Doty
    replied
    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

    Leave a comment:


  • Chris Holbrook
    replied
    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).

    Leave a comment:


  • Fred Buffington
    replied
    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.

    Leave a comment:


  • Dave Biggs
    replied
    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

    Leave a comment:


  • Alan James Taylor
    replied
    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

    Leave a comment:


  • Erich Schulman
    replied
    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.

    Leave a comment:


  • Chris Holbrook
    replied
    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.

    Leave a comment:


  • 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
Working...
X
😀
🥰
🤢
😎
😡
👍
👎