Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner

Creating and writing to word addressable fixed length binary files

Hello,

I need to create and print into a word addressable fixed length binary file. I wanted to ask if anyone knows how I can establish the OPEN statement for the file, specifying the length (let us assume that the length is 512 words). Also, how can I establish the write commands (e.g, the command to write to word 55 of the file). Also, can I use the same CLOSE statement as for non-binary files? I simply need to close the file and save it after I have printed all words.

Many thanks in advance! 

0 Kudos
9 Replies
Highlighted
Black Belt

You want ACCESS='STREAM' and

You want ACCESS='STREAM' and FORM='UNFORMATTED' in your OPEN, which gives you raw binary. Then you can use POS= in the READ/WRITE statements to position within the file. There is no need to specify the length.

Steve (aka "Doctor Fortran") - https://stevelionel.com/drfortran
0 Kudos
Highlighted
Beginner

Thank you Steven, I have one

Thank you Steven, I have one more question: I am also given that "the word size is 8 bytes". Is there a way to define this parameter in my program?

 

0 Kudos
Highlighted
Black Belt

I would have to ask "what is

I would have to ask "what is a word"? Is it an integer, or something else? If an integer, and your word size is limited to sizes of 1, 2, 4 and 8, a proof-of-concept example would be:

    program U780734
    use, intrinsic :: ISO_FORTRAN_ENV
    implicit none

    ! What we want the "word size" to be in bytes
    integer, parameter :: WORD_SIZE = 2
    ! Array of the byte sizes of kinds INT8, INT16, INT32 and
    ! INT64 from ISO_FORTRAN_ENV
    integer, parameter, dimension(4) :: INT_SIZES = [1,2,4,8]
    ! Array of the kinds from ISO_FORTRAN_ENV. Not to be confused
    ! with INTEGER_KINDS that has all supported kinds
    integer, parameter, dimension(4) :: INT_KINDS = [INT8,INT16,INT32,INT64]
    ! Define integer kind whose byte size matches the given word size
    integer, parameter :: K_WORD =  MAXVAL(MERGE(INT_KINDS,[0,0,0,0],(INT_SIZES==WORD_SIZE)))
    
    ! Some values in random order that we will write to the file
    integer(K_WORD), dimension(10) :: values = [integer(K_WORD)::3,6,1,5,9,4,2,8,7,0]
    integer(K_WORD) :: val
    integer :: i
    
    ! Open the file
    open (unit=1,file='test.dat',access='stream',form='unformatted',status='replace')
    ! Write the values each to their position in the file.
    ! Note that Fortran uses 1-origin for positioning
    do i=1,size(values)
        write (1,pos=(values(i)*WORD_SIZE)+1) values(i)
    end do
    
    ! Now read the file sequentially and print the values
    rewind(1)
    do i=1,size(values)
        read (1) val
        print *, val
    end do
    
    end program U780734

There may be a cleaner way to automatically select the proper kind - user Repeat Offender could do this in his sleep, I'd expect. The reason I defined my own INT_KINDS array is that some other compilers (gfortran for example) might support additional kinds.

If your "word" wasn't an integer, then you could just define a derived type of the proper size.

Steve (aka "Doctor Fortran") - https://stevelionel.com/drfortran
0 Kudos
Highlighted
Valued Contributor II

The problem is that user

The problem is that user Repeat Offender can also break the compiler in his sleep. To get the INT_SIZES array one has to create an array via intermediate expressions of different KINDs. Although this is allowed in Fortran, I am not aware of any compiler that can handle it.

program P
   use ISO_FORTRAN_ENV
   use ISO_C_BINDING
   implicit none
   integer I
   integer, parameter :: INT_SIZES(SIZE(INTEGER_KINDS)) = &
      [(SIZE(TRANSFER(INT(0,INTEGER_KINDS(I)),[C_CHAR_'A'])), &
      I=1,SIZE(INTEGER_KINDS))]

   write(*,*) INT_SIZES
end program P

 

0 Kudos
Highlighted
Black Belt

Yes, you can break the

Yes, you can break the compiler in your sleep. While the compiler doesn't "break" when I try your example, when I modify it I get an ICE. I will report this.

However, unless I misunderstood your statement, the standard explicitly prohibits array constructor values of different kinds unless you've included a type-spec. I'll quote from 18-007:

C7111 (R770) If type-spec is omitted, each ac-value expression in the array-constructor shall have the same declared type and kind type parameters.

Intel Fortran has an extension where it allows mixed kinds, and converts everything to the "highest kind". Same for character values of different lengths.

In this case, it doesn't matter if the type-spec is provided, the compiler still gripes that it doesn't think the constructor has a constant size.

Steve (aka "Doctor Fortran") - https://stevelionel.com/drfortran
0 Kudos
Highlighted
Valued Contributor II

Well Steve, as I say this is

Well Steve, as I say this is one instance where the standards committee may have bitten off more than the vendors can chew. Although

INT(0,INTEGER_KINDS(I))

runs the gamut of all integer KINDs, after that SIZE inquiry function operates it spits out a default INTEGER. Thus each ac-value expression really is just a default INTEGER.

But to handle an ac-implied-do like this I think the compiler pretty much has to expand it completely at compile time which would mean that it would have to detect that it's being challenged by an array constructor like this and then carry out the expansion when necessary. I don't think it would be pretty if all ac-implied-dos were expanded at compile time :)

So this is, in my view, one of the warts on the soft underbelly of the standard, which, along with copy-in/copy-out in the presence of possible read only memory is awkward for vendors to implement in any reasonable way.

BTW, your avatar and those of many others has reverted to a blue pawn. I can't seem to update mine to a more physically fit image. However, one seemingly brand new user seems to have uploaded a new one, so what gives?

0 Kudos
Highlighted
Black Belt

Yes, ac-implied-dos do have

Yes, ac-implied-dos do have to get expanded at compile-time. It isn't pretty. F2018 makes it worse as a lot more functions are allowed in constant expressions.

As for copy-in/copy-out, unless the thing you're passing is INTENT(IN) or PROTECTED, the compiler has no way of knowing it shouldn't copy out. But yeah, the standard does, in places, make compiler writers stand on their heads. It's a perspective I hope to bring to future standard development efforts.

That avatar issue seems to come and go. I wish I understood why they keep breaking the forum software. I'll report it again.

Steve (aka "Doctor Fortran") - https://stevelionel.com/drfortran
0 Kudos
Highlighted
Valued Contributor II

You don't need f2018 to trash

You don't need f2018 to trash the compiler. Looking in Metcalf and Reid Fortran 90 Explained, p. 132:

A constant expression is an expression in which each operation is intrinsic and each primary is ... ii) an array constructor whose expressions (including bounds and strides) have primaries that are constant expressions or implied-DO variables.

and in p. 132, TRANSFER is allowed in initialization expressions, so the above example would be valid f90 if the user provided appropriate modules. Of course this still isn't good enough because you need an array reduction function for our application and none were allowed in initialization expressions until the floodgates were opened with f2003. If you knew a hard upper bound the size(A) you could fake MAXVAL via some syntax such as

integer, parameter A_step1(max(size(A)-1,1)) = [max(A(1),A(size(A))),A(2:size(A)-1)]
integer, parameter A_step2(max(size(A_step1)-1,1)) = [max(A_step1(1),A_step1(size(A_step1))),A_step1(2:size(A_step1)-1)]
! Repeat as necessary
integer, parameter :: Amax = A_stepn(1)

But that could get tedious real quick, not to mention that before f2003 you couldn't use the REAL intrinsic in initialization expressions. Oh, you could fake it with

type fake_real
   real x
end type fake_real
integer, parameter :: i = 10**6
real, parameter :: x = transfer(fake_real(i),0.0)

But that wouldn't help either because what we need is the form of the REAL intrinsic with the optional KIND= argument to construct any real number from an expression for its KIND. Again, given a hard upper bound for the number of real KINDs it could be done, horribly. All in all, initialization expressions were quite limited before f2003.

If they are going to take away avatars at least they could make the defaults depend on belt level: red could be a knight, brown a bishop, and so on :)

0 Kudos
Highlighted
Black Belt

I reported the avatar problem

I reported the avatar problem.

Steve (aka "Doctor Fortran") - https://stevelionel.com/drfortran
0 Kudos