Skip to content

Commit ac83acc

Browse files
committed
New utility function "isnum()"
1 parent a88cb9d commit ac83acc

File tree

2 files changed

+103
-4
lines changed

2 files changed

+103
-4
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Parsing features:
3434
* Parse short options without argumentss and they can be embraced (e.g.: -xv)
3535
* Parse long option without argument (e.g: --longarg)
3636
* Parse long option with argument (e.g.: --arg 5.0 or --arg=5.0)
37+
* ***NEW*** Utility function checking numbers in option's arguments
3738

3839
## Requirements
3940

@@ -104,9 +105,7 @@ Put `f90getopt.f90` and your sample program from above (let's say `f90getopt_sam
104105
gfortran f90getopt.F90 f90getopt_sample.f90 -o f90getopt_sample
105106
```
106107

107-
(you can omit `.exe` in `-o f90getopt_sample` on Windows)
108-
109-
to compile it.
108+
(you can omit `.exe` in `-o f90getopt_sample` on Windows) to compile it. Be advised that the capital F90 in the file extention of f90getopt is mandatory.
110109

111110
### Run the sample program
112111

@@ -131,6 +130,8 @@ Output is:
131130
option beta/b=23.2
132131
```
133132

133+
A complete sample program with all features is provided in the [Wiki](https://github.com/haniibrahim/f90getopt/wiki/Full-working-example)
134+
134135
## Changelog
135136

136137
| Version | Description |
@@ -141,6 +142,7 @@ Output is:
141142
| 1.0.2 | Bug in README.md fixed |
142143
| 1.0.3 | Bug in README.md (sample code section) fixed |
143144
| 1.0.4 | Portable declaration of stdin/out/err fixed, minor refactoring and documentation, => GPL 3.0, Wiki page |
145+
| 1.1.0 | New utility function "isnum()" checks for numbers in option's arguments, More sophisticated sample program in the Wiki |
144146

145147
## License
146148

f90getopt.F90

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ module f90getopt
4646
! ------------------ Implicit -----------------------------------------------------------------------------------------------------
4747
IMPLICIT NONE
4848
! ------------------ Local declarations -------------------------------------------------------------------------------------------
49-
PUBLIC :: getopt, option_s, optarg
49+
PUBLIC :: getopt, option_s, optarg, isnum
5050
PRIVATE ! all other are private (hidden)
5151
! ------------------ Constant declarations ----------------------------------------------------------------------------------------
5252

@@ -247,4 +247,101 @@ character function process_short( optstring, arg )
247247
endif
248248
end function process_short
249249

250+
! ----------------------------------------
251+
! Utility function(s)
252+
! ----------------------------------------
253+
254+
integer function isnum (txtval)
255+
! Verify whether a character string represents a numerical value
256+
!
257+
! Can be used to check "optarg" for numbers. Can distinguish
258+
! integer, real/double and character strings:
259+
!
260+
! isnum = 0 => txtval is a string
261+
! isnum = 1 => txtval is a integer
262+
! isnum > 1 => txtval is a real/double
263+
264+
character(len=*), intent(in) :: txtval
265+
266+
! Declaration local constants
267+
integer, parameter :: CINT = 1 ! when txtval contains integer
268+
integer, parameter :: CREAL = 2 ! when txtval contains real
269+
integer, parameter :: CREXP = 3 ! when txtval contains real (exponential)
270+
271+
! Declaration local variables
272+
integer :: num ! numerical indicator variable, if > 0 (0 >= num >= CREXP)
273+
logical :: isint ! integer indicator, if .true.
274+
logical :: isexp ! real with exponent indicator, if .true.
275+
logical :: issign ! sign (+/-) indicator, if .true.
276+
logical :: issignexp ! sign (+/-) indicator for exponents, if .true.
277+
logical :: isblank ! indicator for blanks between characters
278+
integer :: i ! control variable (index), max. len(txtvar)
279+
280+
! Initialize local variables
281+
num = 0
282+
isint = .false.
283+
isexp = .false.
284+
issign = .false.
285+
issignexp = .false.
286+
isblank = .false.
287+
i = 0
288+
289+
! loop over characters
290+
do
291+
if (i >= len(txtval)) then
292+
! last check
293+
if (isint .eqv. .false.) exit
294+
if (num >= CREXP .AND. (isexp .eqv. .false.)) exit
295+
isnum = num
296+
return
297+
end if
298+
299+
i = i + 1
300+
301+
select case (txtval(i:i))
302+
! process blanks
303+
case (' ')
304+
if (num == 0 .and. (isblank .eqv. .false.)) then ! preceding or trailing blanks
305+
continue
306+
else if (num /= 0) then ! blank after sign or number
307+
isblank = .true.
308+
end if
309+
! process digits
310+
case ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
311+
if (num == 0) num = CINT ! first number
312+
if (num < CREXP) then ! no exponent number
313+
isint = .true.
314+
else ! exponent number
315+
isexp = .true.
316+
end if
317+
if (isblank .eqv. .true.) exit ! if blanks are in the middle => string
318+
! process signs
319+
case ('+', '-')
320+
if (num == 0) then ! sign of number
321+
if (issign .eqv. .true.) exit ! second sign without number => string
322+
issign = .true.
323+
num = CINT
324+
else ! sign of exponent
325+
if (num < CREXP) exit
326+
if (issignexp .eqv. .true.) exit
327+
issignexp = .true.
328+
end if
329+
! process decimal point
330+
case ('.')
331+
if (num /= CINT .AND. i /= 1) exit
332+
num = CREAL
333+
! process exponent
334+
case ('e', 'E', 'd', 'D')
335+
if (num >= CREXP) exit
336+
if (isint .eqv. .false.) exit
337+
num = CREXP
338+
case DEFAULT ! any other character means the string is non-numeric
339+
exit
340+
end SELECT
341+
end DO
342+
343+
isnum = 0 ! if this point is reached, the string is non-numeric
344+
RETURN
345+
end function isnum
346+
250347
end module f90getopt

0 commit comments

Comments
 (0)