From: Bart Vandewoestyne on
Hello all,

After switching jobs and now mainly being programming in Octave,
it is nice to be back for asking yet another questions here to
good old clf :-)

I would like to read a configuration file that could look like
this:

nb_apples 3
my_weight 68.5
elephant_weight 4.6e3
smoking_allowed true
name TestName

nb_apples, my_weight, elephant_weight, smoking_allowed and name
are variables (of the appropriate data type) in my Fortran 95 program.
During initialization of my program, I want to read the config
file and assign the variables the values that are specified in the config
file.

Currently, I only work with integers and real numbers (no
logicals or character arrays yet) and I read my datafile like
this:

read(unit=1, fmt=*, iostat=ios) variable_name, variable_value

where variable_name is a character array and variable_value is
being read as a real.

Using a very long switch/case statement, I then assign the value
to the variable depending on what i read in variable_name. If I
have an integer variable, I simply floor the real number and then
assign the value. So for now, I use 'little tricks' and can't
work with strings.

Now... something inside of me tells me there's a better way that
would also allow me to assign logicals and strings. Therefore, I
would have to be able to read *only* variable_name, then use a
switch/case statement to decide what variable I'll have to
assign, and once i know what variable I'm working with, I could
read the second part of the line and assign it to the variable of
the correct data type.

My question is then: if a line in my config file consists of two
things separated by whitespace, how do I read the first thing as
a character array and then a bit later the second thing of the
appropriate data-type?

Thanks,
Bart

--
"Share what you know. Learn what you don't."
From: Reinhold Bader on
Hello,

It appears to me that the BACKSPACE(<unit>) statement should do the
job. There is a restriction in place which says backspacing over list-directed
or namelist-written output is not possible, but I assume you're not writing
to your input file in one of these modes on the same unit.

Here's a sample:

implicit none
character(len=10) :: type
logical :: ll
integer :: il
real :: rl
open(unit=22, file='BBB', form='formatted', status='OLD')
do
read(22, fmt=*, end=100) type
backspace(22)
if (adjustl(type)=='real') then
read(22, fmt=*) type, rl
write(*, *) 'rl = ', rl
else if (adjustl(type)=='integer') then
read(22, fmt=*) type, il
write(*, *) 'il = ', il
else if (adjustl(type)=='logical') then
read(22, fmt=*) type, ll
write(*, *) 'll = ', ll
else
read(22, fmt=*) type
write(*,*) 'Not covered'
end if
end do
100 continue
close(22)
end


Regards
Reinhold
From: Arjen Markus on
On 15 sep, 14:23, Bart Vandewoestyne
<MyFirstName.MyLastN...(a)telenet.be> wrote:
> Hello all,
>
> After switching jobs and now mainly being programming in Octave,
> it is nice to be back for asking yet another questions here to
> good old clf :-)
>
> I would like to read a configuration file that could look like
> this:
>
> nb_apples       3
> my_weight       68.5
> elephant_weight 4.6e3
> smoking_allowed true
> name            TestName
>
> nb_apples, my_weight, elephant_weight, smoking_allowed and name
> are variables (of the appropriate data type) in my Fortran 95 program.
> During initialization of my program, I want to read the config
> file and assign the variables the values that are specified in the config
> file.
>
> Currently, I only work with integers and real numbers (no
> logicals or character arrays yet) and I read my datafile like
> this:
>
> read(unit=1, fmt=*, iostat=ios) variable_name, variable_value
>
> where variable_name is a character array and variable_value is
> being read as a real.
>
> Using a very long switch/case statement, I then assign the value
> to the variable depending on what i read in variable_name.  If I
> have an integer variable, I simply floor the real number and then
> assign the value.  So for now, I use 'little tricks' and can't
> work with strings.
>
> Now... something inside of me tells me there's a better way that
> would also allow me to assign logicals and strings.  Therefore, I
> would have to be able to read *only* variable_name, then use a
> switch/case statement to decide what variable I'll have to
> assign, and once i know what variable I'm working with, I could
> read the second part of the line and assign it to the variable of
> the correct data type.
>
> My question is then: if a line in my config file consists of two
> things separated by whitespace, how do I read the first thing as
> a character array and then a bit later the second thing of the
> appropriate data-type?
>
> Thanks,
> Bart
>
> --
>         "Share what you know.  Learn what you don't."

You can read the line in a single string and then
split it into the two items:

integer :: ivalue
character(len=100) :: line, keyword

read( 10, '(a)' ) line
!
! Identify the keyword and thus the type of data
!
read( line, * ) keyword

if ( keyword == "nb_apples" ) then
!
! Integer value ...
!
read( line, * ) keyword, ivalue
endif

! And so on


Regards,

Arjen
From: Bart Vandewoestyne on
On 2009-09-15, Arjen Markus <arjen.markus895(a)gmail.com> wrote:
>
> You can read the line in a single string and then
> split it into the two items:
>
> integer :: ivalue
> character(len=100) :: line, keyword
>
> read( 10, '(a)' ) line
> !
> ! Identify the keyword and thus the type of data
> !
> read( line, * ) keyword
>
> if ( keyword == "nb_apples" ) then
> !
> ! Integer value ...
> !
> read( line, * ) keyword, ivalue
> endif
>
> ! And so on
>
>
> Regards,
>
> Arjen

Thanks Arjen.

In the meanwhile, I was able to program myself my own
proof of concept:

program config_reader

character(len=20) :: variable_name
character(len=20) :: variable_value

character(len=20) :: name
integer :: nb_apples
real :: my_weight, elephant_weight
logical :: smoking_allowed
integer :: mysize

open(unit=1, file="test.dat", action="read")
do
read(unit=1, fmt=*, iostat=ios) variable_name, variable_value
if (ios /= 0) then
exit
end if

if (variable_name == "nb_apples") then
read(variable_value, fmt=*) nb_apples
print *, "nb_apples = ", nb_apples
end if
if (variable_name == "my_weight") then
read(variable_value, fmt=*) my_weight
print *, "my_weight = ", my_weight
end if
if (variable_name == "elephant_weight") then
read(variable_value, fmt=*) elephant_weight
print *, "elephant_weight = ", elephant_weight
end if
if (variable_name == "smoking_allowed") then
read(variable_value, fmt=*) smoking_allowed
print *, "smoking_allowed = ", smoking_allowed
end if

end do

end program config_reader


It seems like I'm doing it similar as you, but if anybody things this
is a bad approach and has an even more elegant solution, i would be happy
to hear!

Up until now, I didn't know about the read(variable_value, fmt=*) trick!

So *again*, I learned something from clf :-)

Regards,
Bart

--
"Share what you know. Learn what you don't."
From: e p chandler on
On Sep 15, 9:12 am, Bart Vandewoestyne
<MyFirstName.MyLastN...(a)telenet.be> wrote:
> On 2009-09-15, Arjen Markus <arjen.markus...(a)gmail.com> wrote:
>
> > You can read the line in a single string and then
> > split it into the two items:
>
> > integer :: ivalue
> > character(len=100) :: line, keyword
>
>   character(len=20) :: variable_name
>   character(len=20) :: variable_value
>
>   character(len=20) :: name
>   integer :: nb_apples
>   real    :: my_weight, elephant_weight
>   logical :: smoking_allowed
>   integer :: mysize
>
>   open(unit=1, file="test.dat", action="read")
>   do
>     read(unit=1, fmt=*, iostat=ios) variable_name, variable_value
>     if (ios /= 0) then
>       exit
>     end if
>
>     if (variable_name == "nb_apples") then
>       read(variable_value, fmt=*) nb_apples
>       print *, "nb_apples = ", nb_apples
>     end if
>     if (variable_name == "my_weight") then
>       read(variable_value, fmt=*) my_weight
>       print *, "my_weight = ", my_weight
>     end if
>     if (variable_name == "elephant_weight") then
>       read(variable_value, fmt=*) elephant_weight
>       print *, "elephant_weight = ", elephant_weight
>     end if
>     if (variable_name == "smoking_allowed") then
>       read(variable_value, fmt=*) smoking_allowed
>       print *, "smoking_allowed = ", smoking_allowed
>     end if
>
>   end do
>
> end program config_reader
>
> It seems like I'm doing it similar as you, but if anybody things this
> is a bad approach and has an even more elegant solution, i would be happy
> to hear!
>
> Up until now, I didn't know about the read(variable_value, fmt=*) trick!
>
> So *again*, I learned something from clf :-)
>
> Regards,
> Bart

Greetings. It's hard to believe that during all of the time you were
an active member of this newsgroup, you never had need for "INTERNAL
READ" or "INTERNAL WRITE" [smile]. It's one of the FAQs.

One Fortran feature that I do not use but that might help you is a
NAMELIST. Using it would involve changing the format of your config
file. Essentially it sets up a group of named variables. Then you can
specify name/value pairs in its particular format and Fortran takes
care of the multi-way branch logic.

-- Elliot