Fortran Getting inputs from a file - file

I have a Fortran code that gets integers from users, sorts them in descending order with bubble sort, then writes it into a file. However, in the beginning, instead of getting the integers from users, I have to get them from a file. How can I do that? Could you please help me? Thanks.
PROGRAM project
IMPLICIT NONE
INTEGER array(1000),t,p,c
PRINT*,"Enter 1000 element array"
READ*,array
c=1
OPEN(UNIT=25,FILE="sorted.txt")
DO p=1,999
DO c=1,999
IF (array(c)>array(c+1)) then
t=array(c)
array(c)=array(c+1)
array(c+1)=t
ENDIF
ENDDO
ENDDO
WRITE(98,*) array
CLOSE(98)
PRINT*,array(2:999)
END PROGRAM

Before I start, you open the file to write the output to as unit 25:
OPEN(UNIT=25,FILE="sorted.txt")
But you write and close unit 98:
WRITE(98,*) array
CLOSE(98)
Is this an error in your question, otherwise you might want to investigate.
That said, if you are running the program from a shell like bash, the easiest way is to use the output redirecting:
$ cat unsorted.txt > sort.exe
That way you don't have to change anything in your code.
If you don't want to do that, you need to open and read from the file in your program:
open(unit=24, file='unsorted.txt', action='READ', &
status='OLD', form="FORMATTED")
read(24, *) array
close(24)
This assumes that the file with the unsorted data contains exactly 1000 integers and nothing else.

Related

Read 4D arrays from two binary files and write into a single one

Anyone knows how to read and write two unformatted binary files (each one having a 4-dimensional variable) into a single .bin file with the same dimensions?
I am trying this, but without success (I am new to Fortran):
program teste
implicit none
real, dimension(144,73,12,4) :: air,hgt
integer :: l,k,reclen
real :: irec
inquire(iolength=reclen)air
open(1,file='air.bin',status='old',form='unformatted',access='direct',action='read',recl=reclen)
open(2,file='hgt.bin',status='old',form='unformatted',access='direct',action='read',recl=reclen)
open(3,file='air_hgt.bin',form='unformatted',access='direct',action='write',recl=reclen)
read(1,rec=1)air
read(2,rec=1)hgt
close(1)
close(2)
irec=0
do l=1,4
do k=1,12
irec=irec+1
write(3,rec=irec)air(:,:,k,l)
end do
do k=1,12
irec=irec+1
write(3,rec=irec)hgt(:,:,k,l)
end do
end do
close(3)
end program teste
When I run the code, the compiler never stops running, even though the .bin files are very small (2MB each). So, after a few minutes, I force it to stop running and the file created is very large (hundreds of gigabytes). Doesn't seem to make much sense.

fastest way to read large text file

I am looking to pull certain groups of lines from large (~870,000,000 line/~4GB) text files. As a small example, in a 50 line file I might want lines 3-6, 18-27, and 39-45. Using SO to start, and writing some programs to benchmark with my data, it seems that fortran90 has given me the best results (as compared with python, shell commands (bash), etc...).
My current scheme is simply to open the file and use a series of loops to move the read pointer to where I need and writing the results to an output file.
With the above small example this would look like:
open(unit=1,fileName)
open(unit=2,outFile)
do i=1,2
read(1,*)
end do
do i=3,6
read(1,*) line
write(2,*) line
end do
do i=7,17
read(1,*)
end do
do i=18,27
read(1,*) line
write(2,*) line
end do
do i=28,38
read(1,*)
end do
do i=39,45
read(1,*) line
write(2,*) line
end do
*It should be noted I am assuming buffered i/o when compiling, although this seems to only minimally speed things up.
I am curious if this is the most efficient way to accomplish my task. If the above is in fact the best way to do this with fortran90, is there another language more suited to this task?
*Update: Made sure I was using buffered i/o, manually finding the most efficient blocksize/blockcount. That increased speed by about 7%. I should note that the files I am working with do not have a fixed record length.
You can also try to use sed utility.
sed '3,6!d' yourfile.txt
sed '18,27!d' yourfile.txt
Unix utilities tend to be very optimized and to solve easy tasks like this very fast.
One should be able to do this is most any language, so sticking with the theme here is something that should be close to working if you fix up the typos.
(If I had a fortran compiler on an iPad that would make it more useful.)
PROGRAM AA
IMPLICIT NONE
INTEGER :: In_Unit, Out_Unit, I
LOGICAL, DIMENSION(1000) :: doIt
CHARACTER(LEN=20) :: FileName = 'in.txt'
CHARACTER(LEN=20) :: Outfile = 'out.txt'
CHARACTER(LEN=80) :: line
open(NEWunit=In_Unit, fileName) ! Status or action = read only??
open(NEWunit=Out_Unit, outFile) ! Status or action = new or readwrite??
DoIt = .FALSE.
DoIt(3:6) = .TRUE.
DoIt(18:27) = .TRUE.
DoIt(39:45) = .TRUE.
do i=1,1000
read(I_Unit,*) line
IF(doIt(I)) write(Out_Unit,*) line
end do
CLOSE(In_Unit)
CLOSE(Out_Unit)
END PROGRAM AA

How to use an external file which is including the input data in a Fortran program?

I am new to Fortran and want to ask a very simple question. I got some result form a Finite Element Method software and going to use the file in a program that I am writing in Fortran.
It would be appreciated if someone tell me how to entry the data from an external file into a program, although I am familiar with the OPEN statement, but do not know how to address the external file (like .txt, .dat, .for) through the program.
You can assume that the file is located on the path like this:
D:\temp\data.txt
Fortran 77 example (no stricter tags in your question) in which you write a program that reads user input, applies an invariant transformation to the input and outputs the results of the computation, like this
% cat test.f
read(5,*) x, y
write(6,*) x
write(6,*) y
end
% f77 test.f
% ./a.out
3 4
3.00000000
4.00000000
%
where I have manually input, from the terminal, values for x and y.
Now I have a file, produced by FEM, whose name is data.txt, let's see its content
% cat data.txt
3 4
%
A reasonable assumption that you could make is that fortran programs respect stdin and stdout redirection as all the other programs, but you're not sure until you try it with your hands
% ./a.out < data.txt
3.00000000
4.00000000
%
It works. If you have other questions, could you please try to be more specific?

Potential Dangers of Running Code in Parallel

I am working in OSX and using bash for my shell. I have a script which calls an executable hundreds of times, and each call is independent of the other. Therefore I am going to run this code in parallel. However, each call to the executable appends output to a community text file on a new line.
The ordering of the text file is not of importance (although it would be nice, but totally not worth over complicating since I can just use unix sort command), but what is, is that every call of the executable properly printed to the file. My concern is that if I run the script in parallel that the by some freak accident, two threads will check out the text file, print to it and then save different copies back to the original directory of the text file. Thus nullifying one of the writes to the file.
Does this actually happen, or is my understanding of printing to a file flawed? I don't fully know if this would also be a case by case bases so I will provide some mock code of what is being done in my program below.
Script:
#!/bin/sh
abs=$1
input=$(echo "$abs" | awk '{print 0.004 + 0.005*$1 }')
./program input
"./program":
~~Normal .c file stuff here~~
~~VALUE magically calculated here~~
~~run number is pulled out of input and assigned to index for sorting~~
FILE *fpp;
fpp = fopen("Doc.txt","a");
fprintf(fpp,"%d, %.3f\n", index, VALUE);
fclose(fpp);
~Closing events of program.c~~
Commands to run script in parallel in bash:
printf "%s\n" {0..199} | xargs -P 8 -n 1 ./program
Thanks for any help you guys can offer.
A write() call (like fwrite()) with the append flag set in open() (like during fopen()) is guaranteed to avoid the race condition you describe.
O_APPEND
If set, the file offset shall be set to the end of the file prior to each write.
From: POSIX specifications for open:
opengroup.org open
Race conditions are what you are thinking of.
Not 100% sure but if you simple append to the end of the file rather than opening it and editing it should be right
If you have the option, make your program write to standard output instead of directly to a file. Then you can let the shell merge the output of your programs:
printf "%s\n" {0..199} | parallel -P 8 -n 1 ./program > merged_output.txt
Yeah, that looks like a recipe for disaster. If those processes both hit opening the file at the roughly the same time, only one will "take".
I suggest either (easier) writing to separate files then catting them together when the processing is done, or (harder) sending all results to a consumer process that will write the file for everyone.

Fortran. Keep reading first word of the document until it matches the input.

Good evening!
I am trying to read a text document in Fortran 95 and do some calculations with the values in it. The document has numerous gas names and certain values of 'A' assigned to them. So essentially it look something like this:
Document 1: gas values.
GAS A1 A2
steam 1 2
air 3 4
I want then the user to input a gas name (variable gasNameIn) and implement while loop to keep searching for the gas until it matches the input. So eg. user inputs 'air' and the program starts reading first words until air comes up. It then read values of A1 and A2 and uses them for calculation. What I did for it is that I opened the file as unit 25 and tried the following loop:
do while(gasName .NE. gasNameIn)
read(25, *) gasName
if (gasName .EQ. gasNameIn)
read(25,*) A1, A2
endif
enddo
but I get an error "End of file on unit 25".
Any ideas on how my while loop is wrong? Thank you!
By the first read statement, you read the name correctly, but Fortran then proceeds to the next line (record). Try to use
read(25, *, advance='no') gasName
If your searched gas was on the last line, you get the end of file error. Otherwise you will have an error when reading A1.
you need to read whole lines as strings and process. This is untested but the jist of it:
character*100 wholeline
wholeline=''
do while(index(wholeline,'air').eq.0)
read(unit,'(a)')wholeline
end do
then if you can count on the the first strings taking up ~7 cols like in the example,
read(wholeline(7:),*)ia1,ia2
What happened is that you read the whole line in as "gasName" and tested the whole line to see if it was equivalent to "gasNameIn". It never will be the same if the data is laid out the way you have in your sample, so you will get to the end of your file before you ever get a match. It has been a while since I've written in Fortran, but in the first loop "gasName" will be undefined. Usually that is a no no in programming.
The answer that got in before I could get mine typed in is the way forward with your problem. Give it a go. If you still have some trouble maybe I'll fire up a Fortran compiler and try my hand at Fortran again. (It's been since the early 90's that I've done any.)
CHEERS!
Hi, here is your code modified a bit, you can pitch what you don't need. I just added a couple of lines to make it a self-contained program. For instance, There is a statement which assigns the value "steam" to "gasNameIn". I think you mentioned that you would have the user enter a value for that. I've also added a "write" statement to show that your program has properly read the values. It is a crude kind of "unit test". Otherwise, I've only addressed the part of the program that your question asked about. Check this out:
character (len=5) :: gasName, gasNameIn
gasNameIn = "steam"
open (unit = 25, file = "gasvalues.txt")
do while (gasName .NE. gasNameIn)
read (25, *) gasName
if (gasName .EQ. gasNameIn) then
backspace (25)
read (25,*) gasName, A1, A2
write (*, *) "The gas is: ", gasName, ". The factors are A1: ", A1, "; A2: ", A2
endif
end do
close (25)
end
Ok, here are a couple of notes to help clarify what I've done. I tried to keep your code as intact as I could manage. In your program there were some things that my fortran 95 compiler complained about namely: the "then" is required in the "if" test, and "enddo" needed to be "end do". I think those were the main compiler issues I had.
As far as your code goes, when your program tests that "gasName" is the same as "gasNameIn", then you want to "backup" to that record again so that you can re-read the line. There are probably better ways to do this, but I tried to keep your program ideas the way you wanted them. Oh, yes, one more thing, I've named the input file "gasvalues.txt".
I hope this helps some.
CHEERS!

Resources