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!
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.
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.
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
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.
Well Steve, as I say this is one instance where the standards committee may have bitten off more than the vendors can chew. Although
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?
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.
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 :)