Writing over values in a Direct Access File

I have a direct access file of 81 records. Each record has 22 real data values. For various reasons some of the data values are wrong. I wrote a program (DBMGR_THERMO) to edit and add records.

I enter new values from the keyboard to correct the wrong values and then I write the new value back to the direct access file (THERMO_DA) But it doesn’t just write the new value. It corrupts the record with a bunch of garbage.

The code I use is: WRITE (UNIT=10, REC=RECN) D(RECN,IP)

What am I doing wrong??

if you can show a little reproducer program and perhaps a dump of a line, that might make this possible to answer. Without knowing the type and kind of the variables and the values of the variables shown there are too many possibilities. Try making a program that can read one of the lines and confirm you are getting the right values. Once that is working, change the values of the variables that need changed to the corrected values you input and then right the entire line back out. That will probably help you catch what is going wrong. If you have the program that wrote the files you might want to do an inquire on the file and/or check storage_size of the lines. If possible have both programs write a one-line file and verify the files are the same size. There are some dusty corners where different compiles might have opened the file with different widths because one used a byte as the unit for record lengths, and another used a word (a common problem in the past) or the files are

binary but it was written on a machine with a different endian than you are using now, and so on. So a minimal reproducer that writes a file and then changes lines in it that reproduces the problem would be very helpful for others to help with diagnosing the problem(s).

You are writing back out a complete line, and not just a few values that changed, right?

No, I am trying to write just the value changed and not all the values in the record. Is there a way I can send you a fragment of the code to illistrate what exactly I am trying to do?

Fortran direct access reads and writes records.

If you can generate a small reproducer you can post it here; you can make a small github repository,

there are web sites where you can post data; ideally you could place it on the godbolt site as runnable source and save and share it.

This is a small program that arbitrarily has lines composed of ten integers, but that could be anything.

After creating the dummy file, ALL the contents of line 5 are read back; a value read back is changed, and then the line is written back.

The line chosen is displayed before and after the rewrite to show the third integer value was changed.

As @RonShepard emphasized, you read and write a LINE of a direct access file.

There are tricks you can play to make the file word-addressable if all the values are of the same intrinsic type, where you close and reopen the file with recl= the size needed for one value, but that is a very special case.

There are other tricks to play with direct access, but sticking to the basic generally intended use hopefully the example will clarify where you went wrong. Note you can make a seekable file in Fortran now as well, but that is a very different approach.

program direct_access_example
!   + Write arbitrary dummy direct access file, 10 integer numbers per line 
!   + Read the ENIIRE 5th record and change one of the values read back
t!   + write the ENTIRE line back into the file
    implicit none
    integer                    :: i, j, rec_num, unit, iolength, line(10)
    character(len=*),parameter :: all='(*(g0,1x))'

    inquire(iolength=iolength) line ! going to arbitrarily write 10 integers to a line
    write(*,*)'IOLENGTH=',iolength
    ! Open the file with direct access: fixed-length records
    open(newunit=unit, file='direct.dat', access='direct', form='unformatted', &
    recl=iolength, status='replace')

    ! create dummy file
    do i = 1, 100
        write(unit, rec=i) ( (i-1)*10+j,j=0,9 )
    enddo

    ! show line five
    rec_num=5
    call showrec_num()

    ! change third value of line five and rewrite line
    read(unit, rec=rec_num) line  ! note we read ALL of line five
    line(3)=-1-huge(0)            ! change a value from what we read back
    write(unit, rec=rec_num) line ! write the unchanged and changed values, replacing the entire line

    call showrec_num()            ! show the changed line
    close(unit, status='delete')  ! Close the file
contains 

subroutine showrec_num()
! Read and display record REC_NUM from the file
   read(unit, rec=rec_num) line
   print all, 'Values in record', rec_num, 'is', line
end subroutine showrec_num

end program direct_access_example

Your file is all real values, presumably of the same size. In general I would recommend the method above, but it sounds like your file is that special case where you could open with a record length of a single value, thus creating a file where each line is a single value. This enables you to change any single value individually, but if you go down that path make sure you extensively document you are using the file with a record length different than what the file was created with or you might end of confusing people. It can be a handy trick but use it sparingly if you do.

I thought about mentioning stream access too. The warning for this is that not all file positions are allowed, and the programmer is supposed to use those only returned by an INQUIRE() statement. That restriction seems to make the feature less useful in situations like this where the programmer wants to go to arbitrary positions within the file.

Ron: Thank you much. That was the problem. I was trying to write over just one or two values in a given record. When I changed my code to write the entire record; it worked perfectly.

Thanks, when I did as Ron suggested; write the entire record, it worked perfectly.