isnumber(3f) - [M_strings:TYPE] determine if a string represents a number
(LICENSE:PD)
function isnumber(str,msg)
character(len=*),intent(in) :: str
character(len=:),intent(out),allocatable,optional :: msg
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.
str the string to evaluate as to whether it represents a numeric value
or not
msg An optional message describing the string
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
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
John S. Urban
Public Domain
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
character(len=*), | intent(in) | :: | string | |||
character(len=:), | intent(out), | optional, | allocatable | :: | msg | |
logical, | intent(in), | optional | :: | verbose |
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