Reading complex array and vector from TXT file in FORTRAN90 - input

I'm trying to write a subroutine in a module that I can include in various codes to read data from a given file. I have several codes (numerical algorithms) which will be reading the data from the file.
The file has the following format:
First entry: No. of rows and columns of my array of data (e.g. 720)
First n(=720) entries: entire first row of the matrix A
Second n(=720) entries: entire 2nd row of the matrix A
etc.
Last n(=720) entries: all n entries of vector b
Each entry has two columns, one for the REAL part of the number, the other for the COMPLEX part.
In summary, an example basic input file:
2
-0.734192049E+00 0.711486186E+01
0.274492957E+00 0.378855374E+01
0.248391205E-01 0.412154039E+01
-0.632557864E+00 0.195397735E+01
0.289619736E+00 0.895562183E+00
-0.284756160E+00 -0.892163111E+00
where the first entry says its a 2x2 matrix and 2x1 vector
The first 4 lines are the four entries of the matrix A (left column Real, right column Imag.)
The last 2 lines are the two entries of the vector b (left column Real, right column Imag.)
I have written the following code to try and implement this but it simply outputs the wrong results:
n= 2
A= ( 0.0000000 , 1.08420217E-19) (-9.15983229E-16, 3.69024734E+19) ( 1.26116862E-43, 0.0000000 ) ( 0.0000000 , 0.0000000 )
b= ( 0.0000000 , 1.08420217E-19) ( 0.0000000 , 1.08420217E-19)
With the code:
SUBROUTINE matrix_input(n,A,b)
IMPLICIT NONE
!
INTEGER, INTENT(OUT) ::n !size of matrix to be read
COMPLEX, DIMENSION(:,:), INTENT(OUT), ALLOCATABLE ::A !system matrix to be read
COMPLEX, DIMENSION(:), INTENT(OUT), ALLOCATABLE ::b !RHS b vector to be read
DOUBLE PRECISION ::A_Re,A_Im,b_Re,b_Im !
INTEGER ::i,j
!----------------------------------------------------------
! Subroutine outputs 'n'=size of matrix, 'A'=system matrix
! 'b'= RHS vector
!matrix194.txt
OPEN (UNIT = 24, FILE = "matrix_input_test.txt", STATUS="OLD", FORM="FORMATTED", ACTION="READ")
!Read in size of matrix
READ(24,*) n
ALLOCATE(A(n,n))
ALLOCATE(b(n))
!Read matrix A:
DO i=1,n
DO j=1,n
READ(24,*) A_Re, A_Im
A(i,j)=CMPLX(A_Re,A_Im)
END DO
END DO
!Read RHS vector b:
DO i=((n*n)+1),((n*n)+n)
READ(24,*) b_Re, b_Im
b(i)=CMPLX(b_Re,b_Im)
END DO
CLOSE(UNIT=24)
DEALLOCATE(A,b)
END SUBROUTINE matrix_input
EDIT: Following HPC Mark's insights, I have edited my code, and this yields the correct result, however if there are any commands which could lead to issues later on down the line (e.g. with very large arrays that I will be using) I would very grateful to hear about them!
SUBROUTINE matrix_input(n,A,b)
IMPLICIT NONE
!
INTEGER, INTENT(OUT) ::n !size of matrix to be read
COMPLEX, DIMENSION(:,:), INTENT(OUT), ALLOCATABLE ::A !system matrix to be read
COMPLEX, DIMENSION(:), INTENT(OUT), ALLOCATABLE ::b !RHS b vector to be read
!
COMPLEX, DIMENSION(:), ALLOCATABLE ::temp,A_temp !temp vector of matrix A
DOUBLE PRECISION ::A_Re,A_Im,b_Re,b_Im
INTEGER ::i,j,k
!----------------------------------------------------------
! Subroutine outputs 'n'=size of matrix, 'A'=system matrix
! 'b'= RHS vector
!matrix194.txt
OPEN (UNIT = 24, FILE = "matrix_input_test.txt", STATUS="OLD", FORM="FORMATTED", ACTION="READ")
!Read in size of matrix
READ(24,*) n
!Allocate arrays/vectors
ALLOCATE(A(n,n))
ALLOCATE(b(n))
ALLOCATE(temp(n*n+n))
ALLOCATE(A_temp(n*n))
!Read matrix A & vector b:
!16 characters, 9 decimal places, exponent notation, 2 spaces
DO i=1,(n*n)+n
READ(24, FMT="(E16.9, 2X, E16.9)") A_Re, A_Im
temp(i)=CMPLX(A_Re,A_Im)
END DO
!Select A:
DO i=1,n*n
A_temp(i)=temp(i)
END DO
!Reshape
A=RESHAPE(A_temp, (/n,n/))
!Select b:
k=0
DO i=n*n+1,n*n+n
k=k+1
b(k)=temp(i)
END DO
CLOSE(UNIT=24)
!Do not deallocate A & b otherwise won't return anything properly
DEALLOCATE(temp, A_temp)
END SUBROUTINE matrix_input
RESULTS FROM THE EDITED CODE:
n= 2
A= (-0.73419207 , 7.1148620 ) ( 0.27449295 , 3.7885537 ) ( 0.24839121 , 4.1215405 ) (-0.63255787 , 1.9539773 )
b= ( 0.28961974 , 0.89556217 ) (-0.28475615 ,-0.89216310 )

High Performance Mark has already identified the significant issue, some other notes...
Your format specification in your updated code suggests two spaces in between numbers. Your example input suggests one - be mindful of the (optional) leading sign on the second number!
A and b are allocatable dummy arguments. That's a widely supported and very useful Fortran 2003 feature. You are well beyond Fortran 90! If that is unintentional then you will need to seriously redesign things, otherwise...
Being beyond Fortran 90 is a good thing - Fortran 95 (which is the minimum level of standard support offered by all current mainstream Fortran compilers - Fortran 90 is practically obsolete) and Fortran 2003 by extension fixed a serious deficiency in Fortran 90 - as of Fortran 95 local allocatable objects are deallocated automatically when the return or end statement of a procedure is executed. Your deallocate statement at the end is therefore harmless but redundant.
You read in the components of each complex number into double precision (note a common source code style used for modern Fortran tends to avoid the DOUBLE PRECISION type specifier - it is just a synonym for REAL(KIND(0.0D0))). You then store them into default (single precision) complex. Is that intentional? If so, its harmless but a little pointless/inconsistent, otherwise if you intended for the real and imaginary components in the output arrays to be stored at higher precision, then you need to change the declaration of the complex arrays appropriately. All kinds available for REAL must be available for COMPLEX, so your declaration could be COMPLEX(KIND(0.0D0)) (typically you would have the kind as a named constant).
In an input-output list, a complex scalar variable represents two effective items - the real part followed by the imaginary part. Consequently your double precision variables A_Re and A_Im, etc., are somewhat superfluous... READ(24, FMT="(E16.9, 2X, E16.9)") temp(i) is all that is required.
Personally I wouldn't bother with the other temporary arrays - once you know the size of the input data (the first line) allocate your A and B arrays to the necessary size and read directly into them. In an io list an array is expanded into its elements in array element order (first subscript varies fastest) - which appears to be the way your file is arranged. You can combine this with what's known as format reversion to eliminate the need for loops.
Upon return from your subroutine the allocatable arrays "know" their shape - there's no need to return that information separately. Again, harmless but redundant.
Consequently - I think your entire subroutine could look something like:
! Read A and B in from a file that has... etc, etc...
! Assuming a module procedure, where the module already has IMPLICIT NONE.
SUBROUTINE matrix_input(A, b)
! Number of rows and columns of A, elements of B.
INTEGER :: n
! Our output data.
COMPLEX(KIND(0.0D0)), INTENT(OUT), ALLOCATABLE :: A(:,:), b(:)
! Number of the logical unit for IO. In F2008 this becomes a variable
! and you use the NEWUNIT specifier.
INTEGER, PARAMETER :: unit = 24
! The name of the file to read the data from.
CHARACTER(*), PARAMETER :: filename = "matrix_input_test.txt"
! Format for the array and vector component of the data.
CHARACTER(*), PARAMETER :: fmt = "(E16.9, 1X, E16.9)"
!*****************************************************************************
! Connect to the file for sequential formatted reading.
OPEN(unit, FILE=filename, STATUS='OLD', ACTION='READ')
READ (unit, *) n ! Get array dimension.
ALLOCATE(A(n,n), b(n)) ! Allocate result arrays.
READ (unit, fmt) A ! Read in A.
READ (unit, fmt) b ! Read in B.
CLOSE (unit) ! Clean up.
END SUBROUTINE matrix_input

Your code confuses me. SUBROUTINE matrix_input declares arrays A and b to be allocatable with intent(out). Just before the END SUBROUTINE statement you go right ahead and deallocate them. What do you expect the subroutine to return to the calling routine ?

Related

Read free format with no advance

In a given file record, I need to read the first two integer elements at first, and then the rest of the line (a large number of real elements), because the assignment depend on the first 2. Suppose the format of the first two integer elements is not really well defined.
The best way to solve the problem could be something:
read(unitfile, "(I0,I0)", advance='no') ii, jj
read(unitfile,*) aa(ii,jj,:)
But it seems to me the "(I0)" specification is not allowed in gfortran.
Basically the file read in unitfile could be something like:
0 0 <floats>
0 10 <floats>
10 0 <floats>
100 0 <floats>
100 100 <floats>
which is hard to be read with any fortran-like fixed field format specification.
Is there any other way to get around this, apparently trivial, problem?
This applies string manipulations to get the individual components, separated by blanks ' ' and/or tabs (char(9)):
program test
implicit none
character(len=256) :: string, substring
integer :: ii, jj, unitfile, stat, posBT(2), pos
real, allocatable :: a(:)
open(file='in.txt', newunit=unitfile, status='old' )
read(unitfile,'(a)') string
! Crop whitespaces
string = adjustl(trim(string))
! Get first part:
posBT(1) = index(string,' ') ! Blank
posBT(2) = index(string,char(9)) ! Tab
pos = minval( posBT, posBT > 0 )
substring = string(1:pos)
string = adjustl(string(pos+1:))
read(substring,*) ii
! Get second part:
posBT(1) = index(string,' ') ! Blank
posBT(2) = index(string,char(9)) ! Tab
pos = minval( posBT, posBT > 0 )
substring = string(1:pos)
string = adjustl(string(pos+1:))
read(substring,*) jj
! Do stuff
allocate( a(ii+jj), stat=stat )
if (stat/=0) stop 'Cannot allocate memory'
read(string,*) a
print *,a
! Clean-up
close(unitfile)
deallocate(a)
end program
For a file in.txt like:
1 2 3.0 4.0 5.0
This results in
./a.out
3.00000000 4.00000000 5.00000000
NOTE: This is just a quick&dirty example, adjust it to your needs.
[This answer has been significantly revised: the original was unsafe. Thanks to IanH for pointing that out.]
I generally try to avoid doing formatted input which isn't list-directed, when I can afford it. There's already an answer with string parsing for great generality, but I'll offer some suggestions for a simpler setting.
When you are relaxed about trusting the input, such as when it's just the formatting that's a bit tricky (or you 're happy leaving it to your compiler's bounds checking), you can approach your example case with
read(unitfile, *) ii, jj, aa(ii, jj, :)
Alternatively, if the array section is more complicated than given directly by the first two columns, it can be by an expression, or even by functions
read(unitfile, *) ii, jj, aa(fi(ii,jj), fj(ii,jj), :fn(ii,jj))
with pure integer function fi(ii,jj) etc. There is even some possibility of having range validation in those functions (returning a size 0 section, for example).
In a more general case, but staying list-directed, one could use a buffer for the real variables
read(unitfile, *) ii, jj, buffer(:) ! Or ... buffer(:fn(ii,jj))
! Validate ii and jj before attempting to access aa with them
aa(.., .., :) = buffer
where buffer is of suitable size.
Your first considered approach suggests you have some reasonable idea of the structure of the lines, including length, but when the number of reals is unknown from ii and jj, or when the type (and polymorphism reading isn't allowed) is not known, then things do indeed get tricky. Also, if one is very sensitive about validating input, or even providing meaningful detailed user feedback on error, this is not optimal.
Finally, iostat helps.

FORTRAN 90 - Input Syntax Error

This should be an easy one.. I can't figure out why my read statement has a syntax error. I have a file 7477 lines long and I want each of those variables to correspond in each line like my format specifies. Any help here would be great. Thanks!
implicit none
integer :: spe, flen = 7477, i
real, dimension (7477):: wnum,s,A,abh
character :: other
integer :: lun = 11
write(*,*) 'Opening File!'
open(lun,file ='h2o_allbands',status = 'old',action ='read')
write(*,*) 'Success!'
17 format (1x,i2,3x,F9.6,1x,E9.3,1x,E9.3,F5.5,A120)
do i = 1, 7477
read(lun,17) spe(i),wnum(i),s(i),A(i),abh(i),other
write(*,*) wnum(i)
end do
The read has spe(i) as an input list item. spe is not declared as an array, so the compiler probably thinks spe(i) is a reference to an integer function. You cannot read "into" the result of a plain integer function.
Perhaps spe should be declared as an array?
Without seeing a line from your input file, it is difficult to say what the exact problem is: However:
First of all, you should not use a format statement when reading entities (unless in special cases), as this can lead to all sort of different errors, if your line is not well formatted for whatever reasons. So just replace the read line with:
read(lun,*) spe(i), wnum(i), s(i), A(i), abh(i), other
If all the lines are read in well apart the last one, then make sure, that you have a newline at the end of the last line.

Reading a character string of unknown length

I have been tasked with writing a Fortran 95 program that will read character input from a file, and then (to start with) simply spit it back out again.
The tricky part is that these lines of input are of varying length (no maximum length given) and there can be any number of lines within the file.
I've used
do
read( 1, *, iostat = IO ) DNA ! reads to EOF -- GOOD!!
if ( IO < 0 ) exit ! if EOF is reached, exit do
I = I + 1
NumRec = I ! used later for total no. of records
allocate( Seq(I) )
Seq(I) = DNA
print*, I, Seq(I)
X = Len_Trim( Seq(I) ) ! length of individual sequence
print*, 'Sequence size: ', X
print*
end do
However, my initial statements list
character(100), dimension(:), allocatable :: Seq
character(100) DNA
and the appropriate integers etc.
I guess what I'm asking is if there is any way to NOT list the size of the character strings in the first instance. Say I've got a string of DNA that is 200+ characters, and then another that is only 25, is there a way that the program can just read what there is and not need to include all the additional blanks? Can this be done without needing to use len_trim, since it can't be referenced in the declaration statements?
To progressively read a record in Fortran 95, use non-advancing input. For example:
CHARACTER(10) :: buffer
INTEGER :: size
READ (unit, "(A)", ADVANCE='NO', SIZE=size, EOR=10, END=20) buffer
will read up to 10 characters worth (the length of buffer) each time it is called. The file position will only advance to the next record (the next line) once the entire record has been read by a series of one or more non-advancing reads.
Barring an end of file condition, the size variable will be defined with the actual number of characters read into buffer each time the read statement is executed.
The EOR and END and specifiers are used to control execution flow (execution will jump to the appropriately labelled statement) when end of record or end of file conditions occur respectively. You can also use an IOSTAT specifier to detect these conditions, but the particular negative values to use for the two conditions are processor dependent.
You can sum size within a particular record to work out the length of that particular record.
Wrap such a non-advancing read in a loop that appropriately detects for end of file and end of record and you have the incremental reading part.
In Fortran 95, the length specification for a local character variable must be a specification expression - essentially an expression that can be safely evaluated prior to the first executable statement of the scope that contains the variable's declaration. Constants represent the simplest case, but a specification expression in a procedure can involve dummy arguments of that procedure, amongst other things.
Reading the entire record of arbitrary length in is then a multi stage process:
Determine the length of the current record by using a series of incremental reads. These incremental reads for a particular record finish when the end of record condition occurs, at which time the file position will have moved to the next record.
Backspace the file back to the record of interest.
Call a procedure, passing the length of the current record as a dummy argument. Inside that procedure have an character variable whose length is given by the dummy argument.
Inside that called procedure, read the current record into that character variable using normal advancing input.
Carry out further processing on that character variable!
Note that each record ends up being read twice - once to determine its length, the second to actually read the data into the correctly "lengthed" character variable.
Alternative approaches exist that use allocatable (or automatic) character arrays of length one. The overall strategy is the same. Look at the code of the Get procedures in the common ISO_VARYING_STRING implementation for an example.
Fortran 2003 introduces deferred length character variables, which can have their length specified by an arbitrary expression in an allocate statement or, for allocatable variables, by the length of the right hand side in an assignment statement. This (in conjunction with other "allocatable" enhancements) allows the progressive read that determines the record length to also build the character variable that holds the contents of the record. Your supervisor needs to bring his Fortran environment up to date.
Here's a function for Fortran 2003, which sets an allocatable string (InLine) of exactly the length of the input string (optionally trimmed), or returns .false. if end of file
function ReadLine(aunit, InLine, trimmed) result(OK)
integer, intent(IN) :: aunit
character(LEN=:), allocatable, optional :: InLine
logical, intent(in), optional :: trimmed
integer, parameter :: line_buf_len= 1024*4
character(LEN=line_buf_len) :: InS
logical :: OK, set
integer status, size
OK = .false.
set = .true.
do
read (aunit,'(a)',advance='NO',iostat=status, size=size) InS
OK = .not. IS_IOSTAT_END(status)
if (.not. OK) return
if (present(InLine)) then
if (set) then
InLine = InS(1:size)
set=.false.
else
InLine = InLine // InS(1:size)
end if
end if
if (IS_IOSTAT_EOR(status)) exit
end do
if (present(trimmed) .and. present(InLine)) then
if (trimmed) InLine = trim(adjustl(InLine))
end if
end function ReadLine
For example to do something with all lines in a file with unit "aunit" do
character(LEN=:), allocatable :: InLine
do while (ReadLine(aunit, InLine))
[.. something with InLine]
end do
I have used the following. Let me know if it is better or worse than yours.
!::::::::::::::::::::: SUBROUTINE OR FUNCTION :::::::::::::::::::::::::::::::::::::::
!__________________ SUBROUTINE lineread(filno,cargout,ios) __________________________
subroutine lineread(filno,cargout,ios)
Use reallocate,ErrorMsg,SumStr1,ChCount
! this subroutine reads
! 1. following row in a file except a blank line or the line begins with a !#*
! 2. the part of the string until first !#*-sign is found or to end of string
!
! input Arguments:
! filno (integer) input file number
!
! output Arguments:
! cargout (character) output chArActer string, converted so that all unecessay spaces/tabs/control characters removed.
implicit none
integer,intent(in)::filno
character*(*),intent(out)::cargout
integer,intent(out)::ios
integer::nlen=0,i,ip,ich,isp,nsp,size
character*11,parameter::sep='=,;()[]{}*~'
character::ch,temp*100
character,pointer::crad(:)
nullify(crad)
cargout=''; nlen=0; isp=0; nsp=0; ich=-1; ios=0
Do While(ios/=-1) !The eof() isn't standard Fortran.
READ(filno,"(A)",ADVANCE='NO',SIZE=size,iostat=ios,ERR=9,END=9)ch ! start reading file
! read(filno,*,iostat=ios,err=9)ch;
if(size>0.and.ios>=0)then
ich=iachar(ch)
else
READ(filno,"(A)",ADVANCE='no',SIZE=size,iostat=ios,EOR=9); if(nlen>0)exit
end if
if(ich<=32)then ! tab(9) or space(32) character
if(nlen>0)then
if(isp==2)then
isp=0;
else
isp=1;
end if
eend if; cycle;
elseif(ich==33.or.ich==35.or.ich==38)then !if char is comment !# or continue sign &
READ(filno,"(A)",ADVANCE='yes',SIZE=size,iostat=ios,EOR=9)ch; if(nlen>0.and.ich/=38)exit;
else
ip=scan(ch,sep);
if(isp==1.and.ip==0)then; nlen=nlen+1; crad=>reallocate(crad,nlen); nsp=nsp+1; endif
nlen=nlen+1; crad=>reallocate(crad,nlen); crad(nlen)=ch;
isp=0; if(ip==1)isp=2;
end if
end do
9 if(size*ios>0)call ErrorMsg('Met error in reading file in [lineread]',-1)
! ios<0: Indicating an end-of-file or end-of-record condition occurred.
if(nlen==0)return
!write(6,'(a,l)')SumStr1(crad),eof(filno)
!do i=1,nlen-1; write(6,'(a,$)')crad(i:i); end do; if(nlen>0)write(6,'(a)')crad(i:i)
cargout=SumStr1(crad)
nsp=nsp+1; i=ChCount(SumStr1(crad),' ',',')+1;
if(len(cargout)<nlen)then
call ErrorMsg(SumStr1(crad)// " is too long!",-1)
!elseif(i/=nsp.and.nlen>=0)then
! call ErrorMsg(SumStr1(crad)// " has unrecognizable data number!",-1)
end if
end subroutine lineread
I'm using Fortran 90 to do this:
X = Len_Trim( Seq(I) ) ! length of individual sequence
write(*,'(a<X>)') Seq(I)(1:X)
You can simply declare Seq to be a large character string and then trim it as your write it out. I don't know how kosher this solution is but it certainly works for my purpose. I know that some compilers do not support "variable format expressions", but there are various workarounds to do the same thing almost as simply.
GNU Fortran variable expression workaround.

Write data to file in columns (Fortran)

I need to write some data to file in Fortran 90. How should I use WRITE (*,*) input to have the values grouped in columns? WRITE always puts a new line after each call, that's the problem.
code example:
open (unit = 4, file = 'generated_trajectories1.dat', form='formatted')
do time_nr=0, N
write (4,*) dble(time_nr)*dt, initial_traj(time_nr)
end do
And now the point is to have it written in separate columns.
You can use implied DO loops to write values as single records. Compare the following two examples:
integer :: i
do i=1,10
write(*,'(2I4)') i, 2*i
end do
It produces:
1 2
2 4
3 6
...
Using implied DO loops it can rewritten as:
integer :: i
write(*, '(10(2I4))') (i, 2*i, i=1,10)
This one produces:
1 2 2 4 3 6 ...
If the number of elements is not fixed at compile time, you can either use the <n> extension (not supported by gfortran):
write(*, '(<n>(2I4))') (i, 2*i, i=1,n)
It takes the number of repetitions of the (2I4) edit descriptor from the value of the variable n. In GNU Fortran you can first create the appropriate edit descriptor using internal files:
character(len=20) :: myfmt
write(myfmt, '("(",I0,"(2I4))")') n
write(*, fmt=myfmt) (i, 2*i, i=1,n)
Of course, it also works with list directed output (that is output with format of *):
write(*, *) (i, 2*i, i=1,10)
This really depends on what data you are trying to write to file (i.e. whether you have a scalar within a loop or an array...). Can you include a description of this in your question?
If your are trying to write a scalar multiple times to the same row then try using non-advancing I/O, passing the keyword argument advance="no" to the write statement, e.g.
integer :: x
do x = 1,10
write(*, fmt='(i4,1x)', advance="no") x
end do
However, be aware of a suprise with non-advancing I/O.
The answer depends on your answer to Chris's question. If you want a single line, then you will have to use non-advancing IO as described by Chris. Without this, with multiple formatted write statement you will always get multiple lines.
Also, you will likely need to use formatted IO instead of list-directed (*) IO. The rules are loose for list-directed IO. Different compilers may produce different output. With many output items, line breaks are likely to keep the output lines from being too long.
Here a format that should work if all of your variables are reals:
write (4, '( *(2X, ES14.6) )', advance="no" )
how about the good old $ edit descriptor:
write(*, fmt='(i4,$)') x
remember to do a write(*,*) after your loop...

Fortran: How do I read the first character from each line of a text file?

this is my first time trying to program in Fortran. I'm trying to write a program that prints the first 1476 terms of the Fibonacci sequence, then examines the first digit of each term and stores the number of 1s, 2s, 3s, ..., 9s that occur in an array.
The problem that I can't seem to figure out is how to read the first digit of each term. I've tried several things but am having difficulty with my limited knowledge of Fortran techniques. I write the terms to a text file and the idea is to read the first digit of each line and accumulate the respective number in the array. Does anyone have any suggestions of how to do this?
Here is my code so far:
(edit: I included the code I have for reading the file. Right now it just prints out 3.60772951994415996E-313,
which seems like an address of some sort, because it's not one of the Fibonacci numbers. Also, it is the only thing printed, I expected that it would print out every line of the file...)
(edit edit: After considering this, perhaps there's a way to format the writing to the text file to just the first digit. Is there a way to set the number of significant digits of a real number to one? :P)
subroutine writeFib(n)
integer :: i
real*8 :: prev, current, newFib
prev = 0
current = 1
do i = 1, n
newFib = prev + current
prev = current
current = newFib
write(7,*) newFib
end do
return
end subroutine
subroutine recordFirstDigits(a)
integer :: openStat, inputStat
real*8 :: fibNum
open(7, file = "fort.7", iostat = openStat)
if (openStat > 0) stop "*** Cannot open the file ***"
do
read(7, *, iostat = inputStat) fibNum
print *,fibNum
if (inputStat > 0) stop "*** input error ***"
if (inputStat < 0) exit ! end of file
end do
close(7)
end subroutine
program test
integer :: k, a(9)
k = 1476
call writeFib(k)
call recordFirstDigits(a)
end program
Although the suggestions were in place, there were also several things that were forgotten. Range of the REAL kind, and some formatting problems.
Anyways, here's one patched up solution, compiled and working, so try to see if this will work for you. I've took the liberty of choosing my own method for fibonacci numbers calculation.
program SO1658805
implicit none
integer, parameter :: iwp = selected_real_kind(15,310)
real(iwp) :: fi, fib
integer :: i
character(60) :: line
character(1) :: digit
integer :: n0=0, n1=0, n2=0, n3=0, n4=0, n5=0, n6=0, n7=0, n8=0, n9=0
open(unit=1, file='temp.txt', status='replace')
rewind(1)
!-------- calculating fibonacci numbers -------
fi = (1+5**0.5)/2.
do i=0,1477
fib = (fi**i - (1-fi)**i)/5**0.5
write(1,*)fib,i
end do
!----------------------------------------------
rewind(1)
do i=0,1477
read(1,'(a)')line
line = adjustl(line)
write(*,'(a)')line
read(line,'(a1)')digit
if(digit.eq.' ') n0=n0+1
if(digit.eq.'1') n1=n1+1
if(digit.eq.'2') n2=n2+1
if(digit.eq.'3') n3=n3+1
if(digit.eq.'4') n4=n4+1
if(digit.eq.'5') n5=n5+1
if(digit.eq.'6') n6=n6+1
if(digit.eq.'7') n7=n7+1
if(digit.eq.'8') n8=n8+1
if(digit.eq.'9') n9=n9+1
end do
close(1)
write(*,'("Total number of different digits")')
write(*,'("Number of digits 0: ",i5)')n0
write(*,'("Number of digits 1: ",i5)')n1
write(*,'("Number of digits 2: ",i5)')n2
write(*,'("Number of digits 3: ",i5)')n3
write(*,'("Number of digits 4: ",i5)')n4
write(*,'("Number of digits 5: ",i5)')n5
write(*,'("Number of digits 6: ",i5)')n6
write(*,'("Number of digits 7: ",i5)')n7
write(*,'("Number of digits 8: ",i5)')n8
write(*,'("Number of digits 9: ",i5)')n9
read(*,*)
end program SO1658805
Aw, ... I just read you need the number of digits stored in to an array. While I just counted them.
Oh well, ... "left as an exercise for the reader ..." :-)
Can you read with a FORMAT(A1)? It's been 20 years so I don't remember the exact syntax.
I wonder why the open statement succeeds when file 7 hasn't been closed. I think you need an endfile statement and/or a rewind statement in between writing and reading.
Paul Tomblin posted what you have to do after you solve your problem in getting reads to work in the first place.
I am getting an 'end of line' runtime error
You don't show the ! code to read here... which makes it kind of difficult to guess what you are doing wrong :-)
Perhaps you need a loop to read each line and then jump out of the loop to a continue statement when there are no more lines.
Something like this:
do
read(7,*,end=10) fibNumber
end do
10 continue
Better still - look at the more modern style used in this revcomp program.
here are some hints:
You don't need to use characters,
much less file i/o for this problem
(unless you forgot to state that a
file must be created).
Therefore, use math to find your statistics. There are lots of resources on Fibonacci numbers that might provide a simplifying insight or at least a way to independently spot check your answers.
Here is a complicated hint in non-Fortran lingo:
floor(10^(frac(log_10(7214989861293412))))
(Put this in Wolfram Alpha to see what it does.)
A simpler hint (for a different approach) is that you can do very
well in Fortran with simple
arithmetic inside of looping
constructs--at least for a first pass at the solution.
Accumulate your statistics as you
go. This advice would even apply to your character-driven approach. (This problem is ideally suited
for coming up with a cute indexing
scheme for your statistics, but some
people hate cute schemes in
programming. If you don't fear
cuteness ... then you can have associative
arrays in Fortran as long as your
keys are integers ;-)
The most important aspect of this
problem is the data type you will
use to calculate your answers. For
example, here's the last number you
will have to print.
Cheers, --Jared