FORTRAN code with ascii data - fortran

I have a data in ASCII format (.txt file) in which date is given in a column in format yearmonthday (i.e.19900601). I want to separate this column into three columns with year, month and date in each column. Can anyone tell how to do this in Fortran? My data file and code is as following:
datum nied
19480501 -1
19480502 -1
19480503 2
19480504 -1
19480505 2
19480506 -1
19480507 -1
19480508 -1
19480509 -1
19480510 -1
19480511 -1
19480512 2
. .
. .
Code:
program ascii_read
!real(kind=8):: rain(np)
real,allocatable:: rain(:)
integer::np=15739
!integer(kind=8)::day(np)
integer,allocatable::day(:)
character(len = 80)::firstline
integer::i,j
integer,allocatable:: year(:)
allocate (year(np-1))
allocate (rain(np))
allocate (day(np))
open(1243,file="11700.text",status="unknown")
open(12,file="11700_output.text",status="unknown")
read(1243,*)firstline
do i=2,np
read(1243,1111)day(i),rain(i)
end do
1111 Format(i6,F5.2)
write(*,*)day
do j = 1,np-1
year(j)=day(j)
end do
write(*,fmt='(i4)')year
1 format(I4)
!write(*,*)year
return
stop
end program
This gives only year separate in a column,NOT month and day. Any idea how to separate month and day from this data file?

you can use a formatted read to explicitly pull out each of the fields:
integer year,month,day,rain
...
read(1234,'(i4,i2,i2,i3)')year,month,day,rain
In your code you use i6 so day(i) holds things like '194805', then rain(i) is read from the remainder of the line (ie. the last two digits of the "date" integer a space and another integer). I don't know what the f5.2 format does with that but it can't be what you want)

You have to analyse the relationship between your input and the output that you want and then implement the relationship; that is how programming works. You have to first know a method to solve the problem by yourself and then teach the computer how to do it.
For this problem, you can simply see that the first 4 digits represent the year, the next two the month and the last 2 the date. To get the first 4, you divide the full number by 10000, it simply reject the last 4 (month and day). You use the modulo operation to get the last four. And do the same to extract the month from the last two.
Define new array variables month and date and allocate them to the same size as day, also add a new integer variable tmp and change your second loop to this:
do j = 1,np-1
year(j)=day(j)/10000
tmp = mod(day(j), 10000)
month(j) = tmp/100
date(j) = mod(tmp,100)
end do
I will also advise you to use free formatting for reading. You can use fixed format for writing to align data and make it easy to visualize.
Go for modern programming when you start. Litteral numbers are not a good idea in a code, so use named constants for file ids. Make sure that you close files when you do not need them anymore. When you are openning file for reading, use status='old', you want the file to be there or you want you program to stop with an appropriate message. When you are using format, use the format parameter of read and write instead of format statement, for example with the name arg fmt as you did at some places. This make it easy to debug. So your program could look like this.
program ascii_read
!real(kind=8):: rain(np)
integer, parameter :: inputId = 1243
integer, parameter :: outputId = 12
real,allocatable,dimension(:):: rain
integer::np=12
!integer(kind=8)::day(np)
character(len = 80)::firstline
integer::i,j, tmp
integer,allocatable,dimension(:):: day, year, month, date
allocate ( year(np-1), rain(np), day(np), month(np), date(np) )
open(inputId,file="11700.text",status="old")
open(outputId,file="11700_output.text",status="unknown")
read(inputId,*)firstline
do i=2,np
read(inputId,*)day(i),rain(i)
end do
close(inputId)
write(*,*) day
do j = 1,np-1
year(j)=day(j)/10000
tmp = mod(day(j), 10000)
month(j) = tmp/100
date(j) = mod(tmp,100)
! just to see what we get.
write(*, *) day(j), year(j), month(j), date(j)
end do
!write(*,fmt='(i4)')year
!1 format(I4)
!write(*,*)year
return
stop
end program
Thank to IanH for the comment, latest version of fortran included a newunit option that takes care of the IO unit number for programmers. This frees you from defining named constant for unit number. If you do not work with a latest version (Some companies do not upgrade often), there is one ready for use in fortranwiki.

A "hybrid" approach of the other two answers is to first read in the data into a buffer and then split it into integers
character(50) buf
integer year, month, day, rain
read( 10, * ) buf, rain
read( buf, "(i4,i2,i2)" ) year, month, day
! or equivalently
! read( buf(1:4), * ) year
! read( buf(5:6), * ) month
! read( buf(7:8), * ) day
Here, list-directed I/O is used to skip possible spaces before the first column, while integers are extracted based on widths. Also, comment lines starting with "#" (if any) can be skipped by inserting if ( buf(1:1) == "#" ) cycle after the first read statement, for example.

Related

One too many new lines in a WRITE statement [duplicate]

This is my code:
Program Output_Format
Implicit none
Integer::k
Integer,parameter:: Br_nn_mre = 5
Character(56),parameter:: FMT_01 = '(1x,"NN_DM:",1x,*("NN_",i2.2,1x))'
Open( 15 , File = 'Output.txt' , Status = 'Unknown' , Action = 'Write' )
Write( 15 , FMT_01 ) ( k , k = 1 , Br_nn_mre )
Close( 15 , Status = 'Keep' )
End Program Output_Format
The content of Output.txt file is:
NN_DM: NN_01 NN_02 NN_03 NN_04 NN_05 NN_
I want to get this content in Output.txt:
NN_DM: NN_01 NN_02 NN_03 NN_04 NN_05
That is, without the trailing NN_
What is wrong with * IN FMT_01 format? For example if I put a 5 in place of * I will get what I want. How can I use the unlimited repeat count and still get the desired output? I won't always know how many times to repeat.
This is related to how formats are processed, and in particular, when a data transfer statement terminates.
For an output statement such as you have, transfer terminates when either:
a data edit descriptor is reached and there is no remaining element in the output list; or
the final closing parenthesis is reached and there is no remaining element in the output list.
In your formats
'(1x,"NN_DM:",1x,*("NN_",i2.2,1x))'
and
'(1x,"NN_DM:",1x,5("NN_",i2.2,1x))'
the single data edit descriptor there is the i2.2. The 1xs are control edit descriptors and the "NN_DM" and "NN_" are character string edit descriptors.
Let's look at how your format is processed in the case of 5 as the repeat count. The first part of the format 1x,"NN_DM:",1x is processed without issue giving output NN_DM: moving us on to 5("NN_",i2.2,1x)). Corresponding to this repeated fragment are five data items, so they are processed (giving output NN_01 NN_02 NN_03 NN_04 NN_5).
The important part is what happens next. After completing this 5(..) part we reach the final closing parenthesis of the format specification and there is no remaining output item, so processing of the format comes to an end.
What's different with the *(..) case?
Well, when we reach the end of *(..) we go back round to the start of that repeated format; we don't move on to the final closing parenthesis.1 That leaves us to process the edit descriptors until we reach a data edit descriptor. This means that "NN_" is processed (resulting in NN_ being output) before we notice that we are out of data items for output.
Coming to the fix: use the colon edit descriptor. The colon edit descriptor acts like a data edit descriptor in the sense that format processing terminates immediately if there is no remaining data item.
Character(56),parameter:: FMT_01 = '(1x,"NN_DM:",1x,*("NN_",i2.2,:,1x))'
Personally, I would write this as
Character(*),parameter:: FMT_01 = '(" NN_DM:",*(" NN_",i2.2,:))'
1 This would be no different if we had 6 as the repeat count; * isn't special except that it is a "very large repeat count".

How to open and read multiple files in Fortran 90

I have some issues about opening and reading multiple files. I have to write a code which reads two columns in n files formatted in the same way (they are different only for the values...). Before this, I open another input file and an output file in which I will write my results. I read other questions in this forum (such as this one) and tried to do the same thing, but I receive these errors:
read(fileinp,'(I5)') i-49
1
devstan.f90:20.24:
fileLoop : do i = 50,52
2
Error: Variable 'i' at (1) cannot be redefined inside loop beginning at (2)
and
read(fileinp,'(I5)') i-49
1
Error: Invalid character in name at (1)
My files are numbered from 1 to n and are named 'lin*27-n.dat' (where n is the index starts from 1) and the code is:
program deviation
implicit none
character(len=15) :: filein,fileout,fileinp
integer :: row,i,h
real :: usv,usf,tsv,tsf,diff
write(*,'(2x,''Input file .......''/)')
read(*,'(a12)') filein
write(*,'(2x,''Output file........''/)')
read(*,'(a12)') fileout
open(unit = 30,File=filein)
open(unit = 20,File=fileout)
fileLoop : do i = 50,52
fileinp = 'lin*27-'
read(fileinp,'(I5)') i-49
open(unit = i,File=fileinp)
do row = 1,24
read(30,*) h,usv,tsv
read(i,*) h,usf,tsf
diff = usf - usv
write(20,*) diff
enddo
close(i)
enddo fileLoop
end program deviation
How can I solve it? I am not pro in Fortran, so please don't use difficult language, thanks.
The troublesome line is
read(fileinp,'(I5)') i-49
You surely mean to do a write (as in the example linked): this read statement attempts to read from the variable fileinp rather than writing to it.
That said, simply replacing with write is probably not what you need either. This will ignore the previous line
fileinp = 'lin*27-'
merely setting to, in turn, "1", "2", "3" (with leading blanks). Something like (assuming you intend that * to be there)
write(fileinp, '("lin*27-",I1)') i-49
Note also the use of I1 in the format, rather than I5: one may want to avoid blanks in the filename. [This is suitable when there is exactly one digit; look up Iw.m and I0 when generalizing.]

New to Fortran, questions about writing to file

I am completely new to Fortran and pretty new to programming in general. I am trying to compile a script someone else has written. This is giving me a few problems. The top half of the code is:
C
C Open direct-access output file ('JPLEPH')
C
OPEN ( UNIT = 12,
. FILE = 'JPLEPH',
. ACCESS = 'DIRECT',
. FORM = 'UNFORMATTED',
. RECL = IRECSZ,
. STATUS = 'NEW' )
C
C Read and write the ephemeris data records (GROUP 1070).
C
CALL NXTGRP ( HEADER )
IF ( HEADER .NE. 'GROUP 1070' ) CALL ERRPRT(1070,'NOT HEADER')
NROUT = 0
IN = 0
OUT = 0
1 READ(*,'(2i6)')NRW,NCOEFF
if(NRW .EQ. 0) GO TO 1
READ (*,'(3D26.18)',IOSTAT =IN) (DB(K),K=1,NCOEFF)
DO WHILE ( ( IN .EQ. 0 )
. .AND. ( DB(2) .LT. T2) )
IF ( 2*NCOEFF .NE. KSIZE ) THEN
CALL ERRPRT(NCOEFF,' 2*NCOEFF not equal to KSIZE')
ENDIF
C
C Skip this data block if the end of the interval is less
C than the specified start time or if the it does not begin
C where the previous block ended.
C
IF ( (DB(2) .GE. T1) .AND. (DB(1) .GE. DB2Z) ) THEN
IF ( FIRST ) THEN
C
C Don't worry about the intervals overlapping
C or abutting if this is the first applicable
C interval.
C
DB2Z = DB(1)
FIRST = .FALSE.
ENDIF
IF (DB(1) .NE. DB2Z ) THEN
C
C Beginning of current interval is past the end
C of the previous one.
CALL ERRPRT (NRW, 'Records do not overlap or abut')
ENDIF
DB2Z = DB(2)
NROUT = NROUT + 1
print*,'Out =', OUT
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K),K=1,NCOEFF)
print*,'Out2 =', OUT
IF ( OUT .NE. 0 ) THEN
CALL ERRPRT (NROUT,
. 'th record not written because of error')
ENDIF
So, when I print "Out" and "Out2" to the screen I find that Out=0 and Out2=110. As it is not longer equal to zero, the program gives me an error. Therefore I am basically wondering about what is happening here:
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K),K=1,NCOEFF)
I assume that 12 refers to the file I have opened (and created), and want to write to. What does the rest of the first brackets do? And what is the point of the second? Does that gives me the information I want to put in my file? In case, where does DB get filled with that?
Generally I am wondering what is going wrong? Why does OUT change value? (I need t
NCOEFF is defined as an Integer in the beginning of the programme, and DB: as DOUBLE PRECISION DB(3000), DB2Z/0.d0/ , so I assume DB is an array of some sort.
In this program, OUT is telling if the write statement was successful or not. (the IOSTAT parameter to the write statement means "I/O status", or input/output status). It returns 0 if the I/O operation was a success, or the number of the error code otherwise. You can find what the error codes mean here.
I'm not familiar with the REC parameter, but a starting place to investigate yourself can be found here.
To quote the handbook REC indicates the record number to be read or written. As advised, see the documentation which accompanies your compiler for further explanation.
(DB(K),K=1,NCOEFF) means 'all the elements in DB from 1 to NCOEFF. You are looking at an io-implied-do statement.
The statement
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K),K=1,NCOEFF)
is, if memory serves me, called an "implied DO-loop". As written, it will write NCOEFF values from array DB, starting at DB(1).
It is called an implied DO-loop because the explicit form would be (in FORTRAN IV, for the ancients: I know it a lot better than the more modern variations) something along the lines of:
DO 10 K=1,NCOEFF
WRITE (12,REC=NROUT+2,IOSTAT=OUT) DB(K)
10 CONTINUE
(Pretend that the first two lines are indented six columns.) This is a DO-loop. The implied DO-loop form lets you put the "loop" right in the input/output statement.
What makes it useful is that you can have multiple arrays, and multiple loops. For a simple example:
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K), DC(K), K=1,NCOEFF)
110 is the error code thrown by the WRITE call. You need to check your FORTRAN RTL (run-time library) reference. It should list the possible error codes. I think 110 means that you're trying to convert a double-precision value to an integer, but the value is bigger than you can store in an integer. Maybe dump the values in DB and see.

opening a file which has date in a formatted manner and get that date to compare

I am programming in c++,i have a requirement as to get the date only ,rest of the components are not needed,
i have contents in my file in the following way:-
2012-03-26T15:05:24.844Z - DEBUG: Logging_Test:test3 : Testing file logger
2012-03-26T15:05:24.844Z->this part is returned from a function GetDateTime(),which has a stringstream sDateStream and returing to the function GetDateTime() a string
My objective is just to get the date from this part and compare it with the system's date and for the system date i m using:-
void FileLogger::date()
{
SYSTEMTIME time;//variable time to get the system time
GetLocalTime(&time);//reference to the timek
int hour = time.wHour;
if (hour > 12) hour -= 12;
int year = time.wYear;
int month = time.wMonth;
int day = time.wDay;
//not returning anything just storing values of day ,month
//& year
}
now i need to make a comparision b/w two dates and if the date in the .txt is smaller than today's date a new file gets created
please help guys i know it is very simple for the masters but for a starter like me it is creating troubles , please provide help in form of some code so that i can understand it
If you've got the line as a string, and you're sure of the format, the
simplest solution would be:
std::string dateString(
line.begin(), std::find( line.begin(), line.end(), 'T' ) );
(I'd still do some extra checking; e.g. that the dateString is exactly
10 characters long, that it only contains digits or '-', etc.)
This is the ISO date format; it has been especially designed to support
comparison directly as a string, so you might want to consider getting
the system date as a string in ISO format as well. (This is very easy
if you use the standard functions time, localtime and strftime.)

Fortran: Read one value at a time from a line

I am using FORTRAN to read in data from an ASCII text file. The file contains multiple data values per line but the number of values per line is not constant.
101.5 201.6 21.4 2145.5
45.6 21.2
478.5
...
Normally after a read statement, Fortran would go to the next line. What I want to be able to do is read one data value at a time. If it hits the end of the line, it should just continue reading on the next line. Is this possible?
As pointed out by IRO-bot in their comment to your question, the answer has already been given by M.S.B. Below I have merely provided some code illustrating that answer (as M.S.B.'s post contained none):
program test
character(len=40) :: line
integer :: success, i, indx, prev, beginning
real :: value
open(1,file='test.txt')
do
read(1,'(A)',iostat=success) line
if (success.ne.0) exit
prev = 1
beginning = 1
do i=1,len(line)
! is the current character one out of the desired set? (if you
! have got negative numbers as well, don't forget to add a '-')
indx = index('0123456789.', line(i:i))
! store value when you have reached a blank (or any other
! non-real number character)
if (indx.eq.0 .and. prev.gt.0) then
read(line(beginning:i-1), *) value
print *, value
else if (indx.gt.0 .and. prev.eq.0) then
beginning = i
end if
prev = indx
end do
end do
close(1)
end program test
When running this program using the sample lines you provided the output is
101.5000
201.6000
21.40000
2145.500
45.60000
21.20000
478.5000
I hope you will find this helpful.