Reputation: 395
Purpose: Create a program that takes two separate files, opens and reads them, assigns their contents to arrays, do some math with those arrays, create a new array with product numbers, print to a new file. Simple enough right?
My input files have comment characters at the beginning. One trouble is, they are '#' which are comment characters for most plotting programs, but not FORTRAN. What is a simple way to tell the computer not to look at these characters? Since I have no previous FORTRAN experience, I am plowing through this with two test files. Here is what I have so far:
PROGRAM gain
IMPLICIT NONE
REAL, DIMENSION (1:4, 1:8) :: X, Y, Z
OPEN(1, FILE='test.out', &
STATUS='OLD', ACTION='READ') ! opens the first file
READ(1,*), X
OPEN(2, FILE='test2.out', &
STATUS='OLD', ACTION='READ') ! opens the second file
READ(2,*), Y
PRINT*, X, Y
Z = X*Y
! PRINT*, Z
OPEN(3, FILE='test3.out', STATUS='NEW', ACTION='WRITE') !creates a new file
WRITE(3,*), Z
CLOSE(1)
CLOSE(2)
CLOSE(3)
END PROGRAM
PS. Please do not overwhelm me with a bunch of code monkey gobblety gook. I am a total programming novice. I do not understand all the lingo, that is why I came here instead of searching for help in existing websites. Thanks.
Upvotes: 3
Views: 52065
Reputation: 29401
If you mean that the comments are only at the beginning of the file, it is fairly simple -- no need to count the comment lines or rewind file -- you can read the lines into a string and test whether they are a comment. Then you will eventually encounter a non-comment line. Problem: it will have been read into a string and thus not available for a regular read ... solution ... use "backspace" to unread one record so that you can now use normal file reads to read the rest of the file. A slightly more complicated solution would be necessary if comment lines were interspersed throughout the file -- as already stated, read the lines into a string, then read from the string.
Here is a worked example ... I have assumed that the "#" is in the first column and various other simplifying assumptions. Some recommendations: put your subroutines and functions into a module and "use" that module -- this will allow the compiler to check the interfaces. As you are developing your programs, use as many code checking and warning options as possible -- especially subscript bounds checking -- it will save time in the end.
P.S. It is officially "Fortran" since Fortran 90 -- it was "FORTRAN" for FORTRAN 77 and earlier.
module read_file_module
implicit none
contains
subroutine read_file (UnitNum, FileName, NumRows, NumCols, Array )
integer, intent (in) :: UnitNum
character (len=*), intent (in) :: FileName
integer, intent (in) :: NumRows, NumCols
real, dimension (1:NumRows, 1:NumCols), intent (out) :: Array
character (len=300) :: line
integer :: i, j
open (unit=UnitNum, file=FileName, status='old', action='read' )
ReadComments: do
read (UnitNum, '(A)') line
if (line (1:1) /= "#") exit ReadComments
end do ReadComments
backspace (UnitNum)
do i=1, NumRows
read (UnitNum, *) (Array (i, j), j=1,NumCols)
end do
close (UnitNum)
return
end subroutine read_file
end module read_file_module
program test_prog
use read_file_module
implicit none
real, dimension (1:8, 1:4) :: Array
integer :: i, j
call read_file (66, 'TestFile.txt', 8, 4, Array)
do i=1, 8
write (*, '( 4(2X, F7.3) )' ) (Array (i, j), j=1,4)
end do
end program test_prog
And some test data to show how flexible the input data can be:
# comment one
# comment two
1.1 2.0 3.0 4.1
1.2 2.0 3.0 4.2
1.3 2.0 3.0 4.3
1.4
2.0 3.0 4.4
1.5 2.0 3.0 4.5
1.6 2.0 3.0 4.6
1.7 2.0 3.0 4.7
1.8 2.0 3.0 4.8
Upvotes: 6
Reputation: 5835
I'm not real familiar with anything beyond FORTRAN 77 but here are a few pointers (and a working version of what you posted in your answer). First the working code (I added line numbers):
1 REAL FUNCTION myfile(unit, file, rows, columns)
2 IMPLICIT NONE
3 INTEGER, INTENT(IN) :: unit, rows, columns
4 CHARACTER(LEN=*) :: file
5 REAL, DIMENSION (1:columns, 1:rows) ::X
6 OPEN(unit, FILE=file, STATUS='OLD', ACTION='READ')
7 READ(unit,*), X
8 PRINT*, X
9 CLOSE(unit)
10 myfile= 0
11 END FUNCTION myfile
12
13 PROGRAM gain
14 errno = myfile(1, "test.out", 8, 4)
15 END PROGRAM
The differences are:
Instead of using a function, you could define this your myfile routine as a subroutine instead. In this case you would definitely need to pass in the array you want filled as a parameter. You wouldn't need line 10 and instead of assigning the return value to a variable in the main program you would 'call' the routine. i.e. line 14 would look like this:
call myfile(1, "test.out",8,4)
EDIT: I posted this and then realized I forgot to answer the original questions. I did so and then for some reason couldn't connect to SO to upload the edits. So here they are finally.
That gets your routine compiling. To actually deal with the comment lines, you have several options (at least these are the ones that come to mind initially). These are in order from simplest/most brittle to more robust/general:
Which method you choose (and other people may have other suggestions) depends on your particular application. Good luck.
Upvotes: 1
Reputation: 309028
Write a subroutine that puts this logic into one spot for you so you can call it for both files. You'll need to read each line as a string and add an IF test to check whether a given line starts with a "#" or not. If the line starts with a "#", just read the next line. If not, convert the string to a value and add it to the array of values you're returning.
Upvotes: 3