isNumber Function

public function isNumber(string, msg, verbose) result(isnumber)

NAME

isnumber(3f) - [M_strings:TYPE] determine if a string represents a number
(LICENSE:PD)

SYNOPSIS

function isnumber(str,msg)

 character(len=*),intent(in)  :: str
 character(len=:),intent(out),allocatable,optional  :: msg

DESCRIPTION

 ISNUMBER(3f) returns a value greater than zero if the string represents
 a number, and a number less than or equal to zero if it is a bad number.
 Blank characters are ignored.

OPTIONS

 str  the string to evaluate as to whether it represents a numeric value
      or not
 msg  An optional message describing the string

RETURNS

 isnumber  the following values are returned

            1 for an integer             [-+]NNNNN
            2 for a whole number         [-+]NNNNN.
            3 for a real value           [-+]NNNNN.MMMM
            4 for a exponential value    [-+]NNNNN.MMMM[-+]LLLL
                                         [-+]NNNNN.MMMM[ed][-+]LLLL

           values less than 1 represent an error

EXAMPLES

As the example shows, you can use an internal READ(3f) along with the IOSTAT= parameter to check (and read) a string as well.

 program demo_isnumber
 use M_strings, only : isnumber
 implicit none
 character(len=256) :: line
 real               :: value
 integer            :: ios1, ios2
 integer            :: answer
 character(len=256) :: message
 character(len=:),allocatable :: description
    write(*,*)'Begin entering values, one per line'
    do
       read(*,'(a)',iostat=ios1)line
       !
       ! try string as number using list-directed input
       line=''
       read(line,*,iostat=ios2,iomsg=message) value
       if(ios2 == 0)then
          write(*,*)'VALUE=',value
       elseif( is_iostat_end(ios1) ) then
          stop 'end of file'
       else
          write(*,*)'ERROR:',ios2,trim(message)
       endif
       !
       ! try string using isnumber(3f)
       answer=isnumber(line,msg=description)
       if(answer > 0)then
          write(*,*) &
          & ' for ',trim(line),' ',answer,':',description
       else
          write(*,*) &
          & ' ERROR for ',trim(line),' ',answer,':',description
       endif
       !
    enddo
 end program demo_isnumber

Example run

> Begin entering values
> ERROR:          -1 End of file
>  ERROR for            -1 :null string
>10
> VALUE=   10.0000000
>  for 10            1 :integer
>20
> VALUE=   20.0000000
>  for 20            1 :integer
>20.
> VALUE=   20.0000000
>  for 20.            2 :whole number
>30.1
> VALUE=   30.1000004
>  for 30.1            3 :real number
>3e1
> VALUE=   30.0000000
>  for 3e1            4 :value with exponent
>1-2
> VALUE=   9.99999978E-03
>  for 1-2            4 :value with exponent
>100.22d-4
> VALUE=   1.00220004E-02
>  for 100.22d-4            4 :value with exponent
>1--2
> ERROR:        5010 Bad real number in item 1 of list input
>  ERROR for 1--2           -5 :bad number
>e
> ERROR:        5010 Bad real number in item 1 of list input
>  ERROR for e           -6 :missing leading value before exponent
>e1
> ERROR:        5010 Bad real number in item 1 of list input
>  ERROR for e1           -6 :missing leading value before exponent
>1e
> ERROR:        5010 Bad real number in item 1 of list input
>  ERROR for 1e           -3 :missing exponent
>1e+
> ERROR:        5010 Bad real number in item 1 of list input
>  ERROR for 1e+           -4 :missing exponent after sign
>1e+2.0
> ERROR:        5010 Bad real number in item 1 of list input
>  ERROR for 1e+2.0           -5 :bad number

AUTHOR

John S. Urban

LICENSE

Public Domain

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: string
character(len=:), intent(out), optional, allocatable :: msg
logical, intent(in), optional :: verbose

Return Value integer


Contents

Source Code


Source Code

function isNumber(string,msg,verbose)

! ident_58="@(#) M_strings isnumber(3f) Determines if a string is a number of not."

character(len=*),intent(in)    :: string
character(len=:),intent(out),allocatable,optional :: msg
logical,intent(in),optional                      :: verbose
integer                      :: isnumber

integer             :: i,iend
character(len=1),allocatable :: z(:)
character(len=:),allocatable :: message
logical                      :: founddigit
logical                      :: verbose_local

   i=1
   founddigit=.false.
   isnumber=0
   z=switch(trim(nospace(string)))
   iend=size(z)
   message='not a number'
   if(present(verbose))then
      verbose_local=verbose
   else
      verbose_local=.false.
   endif
   DONE : block
      if(iend == 0)then
         isnumber=-1                   ! string is null
         message='null string'
         exit DONE
      endif

      if(index('+-',z(i)) /= 0) i=i+1  ! skip optional leading sign
      if(i > iend)then
         isnumber=-2                   ! string was just a sign
         message='just a sign'
         exit DONE
      endif

      call next()                      ! position I to next non-digit or end of string+1

      if(i > iend)then
         isnumber=1                    ! [+-]NNNNNN
         message='integer'
         exit DONE
      endif
      if(z(i) == '.')then              ! a period would be OK at this point
         i=i+1
      endif

      if(i > iend)then                ! [+-]NNNNNN.
         isnumber=2
         message='whole number'
         exit DONE
      endif

      call next()                      ! position I to next non-digit or end of string+1
      if(i > iend)then
         isnumber=3                    ! [+-]NNNNNN.MMMM
         message='real number'
         exit DONE
      endif

      if(index('eEdD',z(i)) /= 0)then
         i=i+1
         if(i == 2)then
            isnumber=-6                   ! [+-]NNNNNN[.[MMMM]]e but a value must follow
            message='missing leading value before exponent'
            exit DONE
         endif
      endif
      if(i > iend)then
         isnumber=-3                   ! [+-]NNNNNN[.[MMMM]]e but a value must follow
         message='missing exponent'
         exit DONE
      endif
      if(.not.founddigit)then
         isnumber=-7
         message='missing value before exponent'
         exit DONE
      endif
      if(index('+-',z(i)) /= 0) i=i+1
      if(i > iend)then
         isnumber=-4                   ! [+-]NNNNNN[.[MMMM]]e[+-] but a value must follow
         message='missing exponent after sign'
         exit DONE
      endif
      call next()                      ! position I to next non-digit or end of string+1
      if(i > iend)then
         isnumber=4                    ! [+-]NNNNNN.MMMMe[+-]LL
         message='value with exponent'
         exit DONE
      endif
      isnumber=-5
      message='bad number'
   endblock DONE
   if(verbose_local)then
      write(*,*)trim(string)//' is '//message
   endif
   if(present(msg))then
      msg=message
   endif

contains
   subroutine next() ! move to next non-digit or end of string+1
      integer :: j
      do j=i,iend
         if(.not.isdigit(z(j)))then
            exit
         endif
         founddigit=.true.
         if(verbose_local) write(*,*)'I=',i,' J=',j,' Z(j)=',z(j)
      enddo
      i=j
      if(verbose_local)then
         write(*,*)'I and J=',i
         if(i <= iend) then
            write(*,*)'Z(I)=',z(i)
         else
            write(*,*)'====>'
         endif
      endif
   end subroutine next
end function isNumber