The following data is given in a file:
1 7 5 4 11 0 1 -13 -7
I want to form a 3x3 matrix from these columns. I have tried advance=no but receive a runtime error.
program form_matrix
implicit none
integer:: col,A(9)
open(unit=12,file='q10.txt')
do col=1,9
read(12,*,advance='no')(A(col))
end do
7 format(3i4)
write(*,7)(A(col),col=1,9)
close(12)
end program
Well, a couple of things:
To read into a variable, you need to specify that variable and not a statement containing the variable. A(col) instead of (A(col)).
Then, you are not allowed to use the list directed format (*) with an advance specifier, you need a format (which would be difficult in your case, because of the different widths of the columns).
It will probably be easier to read the whole line into the array in one go, without the loop and using the list directed format:
program form_matrix
implicit none
integer:: col,A(9)
open(unit=12,file='q10.txt')
read(12,*) A
7 format(3i4)
write(*,7)(A(col),col=1,9)
close(12)
end program
* enables list-directed formatting, where individual values are separated by commas or blanks. The type is determined by the RHS of the statement. So you read in a list of nine (because A is nine elements long) integers (because A is integer). For more details on this I suggest the Fortran 2008 Standard, Cl. 10.10 "List-directed formatting"
Related
So I have some code that does essentially this:
REAL, DIMENSION(31) :: month_data
INTEGER :: no_days
no_days = get_no_days()
month_data = [fill array with some values]
WRITE(1000,*) (month_data(d), d=1,no_days)
So I have an array with values for each month, in a loop I fill the array with a certain number of values based on how many days there are in that month, then write out the results into a file.
It took me quite some time to wrap my head around the whole 'write out an array in one go' aspect of WRITE, but this seems to work.
However this way, it writes out the numbers in the array like this (example for January, so 31 values):
0.00000 10.0000 20.0000 30.0000 40.0000 50.0000 60.0000
70.0000 80.0000 90.0000 100.000 110.000 120.000 130.000
140.000 150.000 160.000 170.000 180.000 190.000 200.000
210.000 220.000 230.000 240.000 250.000 260.000 270.000
280.000 290.000 300.000
So it prefixes a lot of spaces (presumably to make columns line up even when there are larger values in the array), and it wraps lines to make it not exceed a certain width (I think 128 chars? not sure).
I don't really mind the extra spaces (although they inflate my file sizes considerably, so it would be nice to fix that too...) but the breaking-up-lines screws up my other tooling. I've tried reading several Fortran manuals, but while some of the mention 'output formatting', I have yet to find one that mentions newlines or columns.
So, how do I control how arrays are written out when using the syntax above in Fortran?
(also, while we're at it, how do I control the nr of decimal digits? I know these are all integer values so I'd like to leave out any decimals all together, but I can't change the data type to INTEGER in my code because of reasons).
You probably want something similar to
WRITE(1000,'(31(F6.0,1X))') (month_data(d), d=1,no_days)
Explanation:
The use of * as the format specification is called list directed I/O: it is easy to code, but you are giving away all control over the format to the processor. In order to control the format you need to provide explicit formatting, via a label to a FORMAT statement or via a character variable.
Use the F edit descriptor for real variables in decimal form. Their syntax is Fw.d, where w is the width of the field and d is the number of decimal places, including the decimal sign. F6.0 therefore means a field of 6 characters of width with no decimal places.
Spaces can be added with the X control edit descriptor.
Repetitions of edit descriptors can be indicated with the number of repetitions before a symbol.
Groups can be created with (...), and they can be repeated if preceded by a number of repetitions.
No more items are printed beyond the last provided variable, even if the format specifies how to print more items than the ones actually provided - so you can ask for 31 repetitions even if for some months you will only print data for 30 or 28 days.
Besides,
New lines could be added with the / control edit descriptor; e.g., if you wanted to print the data with 10 values per row, you could do
WRITE(1000,'(4(10(F6.0,:,1X),/))') (month_data(d), d=1,no_days)
Note the : control edit descriptor in this second example: it indicates that, if there are no more items to print, nothing else should be printed - not even spaces corresponding to control edit descriptors such as X or /. While it could have been used in the previous example, it is more relevant here, in order to ensure that, if no_days is a multiple of 10, there isn't an empty line after the 3 rows of data.
If you want to completely remove the decimal symbol, you would need to rather print the nearest integers using the nint intrinsic and the Iw (integer) descriptor:
WRITE(1000,'(31(I6,1X))') (nint(month_data(d)), d=1,no_days)
As shown in the following code that I use to practice fixed-form Fortran (because the code I am trying to learn is fixed form Fortran), the 4th format statement and the reading from file 1 are newly added. The code worked as expected prior to adding these statements, but now the intel ifort compiler with no additional flags will not compile the 4th format statement and returns the following error:
fortPractice.for(18): error #5082: Syntax error, found FORMAT_ELEMENT 'format' when expecting one of: <END-OF-STATEMENT> ;
4 format(i6,2x)
--------------^
fortPractice.for(26): error #6052: This label has not been defined as a FORMAT label.
write(2,3)
----------------------------------^
The code is:
c This is a script for practicing Fortran codes
program fortPractice
implicit none
integer :: x(0:5),y(2:7)
integer :: nph(1:6)
real :: z(4:9)
integer :: i
OPEN(unit=1,file='test.txt',status='old')
read(1,*) nph
close(unit=1)
open(unit = 2, file = 'output.txt')
2 format(i3,2x,i3,2x,2e11.2)
3 format(1x,78('*'))
4 format(i6,2x)
do i = 0,5
x(i) = i;
y(i+2) = i+2;
z(i+4) = x(i)**2 + y(i+2)**2
z(i+4) = sqrt(z(i+4)) + 10000
write(2,2) x(i),y(i+2),z(i+4)
write(2,3)
enddo
write(2,*) nph
close(unit = 2)
endprogram fortPractice
Output is a file created by the program. test.txt contains just a row of numbers: 1 2 3 4 5 6
When I comment or remove the 4th format statement then the code compiles and runs as expected. As I am just learning how fixed form Fortran works, I am just interested in why the 4th format statement won't compile.
Edit:
I have replaced all the tabs with spaces, and the program with some more modification shown below can now compile, but the program does not produce the output file, likely encountered some run-time error:
Edit 2:
Nevermind, I forgot to change the file identifiers.
Thanks everyone!
Now we have enough information to solve the problem. In the troublesome source line, the label 4 is preceded by a tab. Tabs in Fortran source are not standard, but Intel Fortran (and many other compilers) support something called "tab source form". The way it works in ifort is as follows:
If the line starts with a tab and then a nonzero digit, the digit is treated as if it were in column 6
If the line starts with a tab and then some other character, then the character is treated as if it were in column 7
If the line starts with a numeric statement label and then a tab, the next character is treated as if it were in column 7
Otherwise, a tab is treated as a blank (this last varies among compilers)
In your case, a tab preceded the 4 so it was taken to be a continuation of the previous line, resulting in an error. Either don't use tabs, or understand how the compiler treats them. Editors that automatically insert tabs will just give you trouble.
Suppose I'm trying to write out a CSV file header that looks like this:
STRING1 2001, 2002, 2003, 2004,
And some variable-format Fortran90 code that does this is
INTEGER X<Y
X=2001
Y=2004
WRITE(6,'(A,(999(5X,I4,",")))') ' STRING1',(y,y=X,Y)
The "999" repeats the (5X,I4,",") format structure as many times as it needs to (up to 999 times, at least) to complete. Assume X and Y are subject to change and therefore the number of loop iterations may also change.
But if I want the header to look like this, with an additional string on the end of the sequence, like
STRING1 2001, 2002, 2003, 2004, STRING2
...I have tried adding another A toward the end of the format string, but that repeated variable format structure apparently doesn't know that it needs to "escape" when the integers are done with and so it errors out.
I can work around this by including 'ADVANCE="no"' in the format string and printing the second string using a new WRITE statement to get what I fundamentally want, but is there a way I can do this all with a single format structure?
[NOTE: no angle-bracket answers please; this is for GNU gfortran, which doesn't support that extension]
C'mon folks, get with the program!
This is standard Fortran 2008:
WRITE(6,'(A,*(5X,G0,:,","))') ' STRING1',(y,y=X,Y), ' STRING2'
I am fairly sure that gfortran supports the "indefinite group repeat count". G format was extended in Fortran 2008 to support any intrinsic data type, and a width of zero means "minimum number of characters." The colon is a F77 feature that stops the trailing comma from being emitted.
With this, ifort gives me:
STRING1 2001, 2002, 2003, 2004, STRING2
FWIW, I am not happy with your reuse of y as the loop control variable, since this is NOT a statement entity and will get set to 2005 at the end of the loop. Use a separate variable, please!
program test
character(len=20) :: N_number
integer :: X,Y
X=2001
Y=2004
write(N_number,*) Y-X+1
write(6,'(A,('//TRIM(N_number)//'(5X,I4,","))A)') ' STRING1',(y,y=X,Y),' STRING2'
end program test
It's a shame that the variable-format extension isn't standard. Since it isn't, most people recommend the approach shown by #anonymous. That is, instead of using <N>, you first convert the integer into a string using an internal-write statement. This string representation of N is then inserted within the format expression to be used in the write or print statements.1
Alternatively, you could convert the numerical values from the array into a string.2 It's also pretty straightforward. In the example below, I've shown both of these approaches.
program writeheader
implicit none
character(len=80) :: string1, string2, string3, fmt, num
integer, dimension(10) :: array
integer :: x,y,len
continue
string1 = "begin"
string3 = "end"
array = [1:10]
x = 3
y = 7
!! Method 1: Convert the variable number of values into a string, then use it
!! to create the format expression needed to write the line.
write(num, "(i)") y - x + 1
fmt = "(a,', ',(" // trim(adjustl(num)) // "(i0:', ')), a)"
print fmt, trim(string1), array(x:y), trim(string3)
!! Method 2: Convert the desired range of array values into a character string.
!! Then concat, and write the entire line as a string.
write(string2, "(*(', ',i0))" ) array(x:y)
len = len_trim(string2) + 1
print "(a)", trim(string1) // string2(1:len) // trim(string3)
end program writeheader
In either case shown in the example, the output looks like: begin, 3, 4, 5, 6, 7, end
1 If I can find it, I'll add a link to a nice solution here on SO that created a function to generate the format expression.
2 I've used the array bounds directly here, as an alternative to implied do-loops.
I am trying to read a list of real numbers from an external file into an array. I do not know how many records fields, separated by spaces, there are per record in a file, and I was therefore planning to use non-advancing I/O to first count the number of records fields, then allocate a real array of sufficient size, and to finally read the file into that same array.
Here is an example input file (where the edit descriptor should be f3.1 for every record field, i.e. a float 3 characters wide with one decimal, and counting the dot; although if I read Metcalf et al. correctly, the decimal is ignored):
1.0 2.0 3.0 4.0 5.0
And a MWE of my program looks like this
program testread
use iso_fortran_env
implicit none
character(len=255) :: filename
filename = 'read.dat'
print *, count_entries(filename)
contains
integer function count_entries(coefficient_file) result(n)
character(len=*), intent(in) :: coefficient_file
!real, dimension(:), allocatable :: coefficients
integer :: fileunit, stat
real :: temp
n=0
open(newunit=fileunit, file=coefficient_file, iostat=stat)
do
read(fileunit,'(f3.1)',advance='no',iostat=stat) temp
if (stat == iostat_end) then
exit
else
n = n + 1
end if
print *, stat, temp
end do
close(fileunit)
! What should happen in the future...
!allocate(coefficients(n))
!read(fileunit,*,iostat=stat) coefficients
end function count_entries
end program testread
If you save the sample input above as read.dat, compile the program with gfortran -o testread{,.f90} and execute it, you will get the following result:
0 1.00000000
0 2.00000000
0 0.300000012
0 0.00000000
0 4.00000000
0 5.00000000
-2 0.00000000
7
In other words, instead of counting 5 entries, it counts 7. And this is not surprising as it, for some reason, sees 7 numbers. But I wonder: why does it see 7 numbers? How can I extend my function to a) be able to also read larger reals and b) read reals of non-uniform width? For example, I would like to be able to read 1.01 1.003 2.1, etc.
It sees six numbers (the last one is an end of record condition) because your format specification specifies that three characters be read each time, but your data is spaced every four columns (three for the data, one for the separating blank).
If your input isn't fixed format (number of columns may vary), then read the whole record (line) into a character(:), allocatable variable and then manually chop that string up.
(Never use a format specification with a specified number of decimal places for input unless you know that your input will always suit that.)
You forgot to account for the spaces. Just use '(f3.1,1x)'.
I am writing some simple output in fortran, but I want whitespace delimiters. If use the following statement, however:
format(A20,ES18.8,A12,ES18.8)
I get output like this:
p001t0000 3.49141273E+01obsgp_oden 1.00000000E+00
I would prefer this:
p001t0000 3.49141273E+01 obsgp_oden 1.00000000E+00
I tried using negative values for width (like in Python) but no dice. So, is there a way to left-justify the numbers?
Many thanks in advance!
There's not a particularly beautiful way. However, using an internal WRITE statement to convert the number to a text string (formerly done with an ENCODE statement), and then manipulating the text may do what you need.
Quoting http://rsusu1.rnd.runnet.ru/develop/fortran/prof77/node168.html
An internal file WRITE is typically
used to convert a numerical value to a
character string by using a suitable
format specification, for example:
CHARACTER*8 CVAL
RVALUE = 98.6
WRITE(CVAL, '(SP, F7.2)') RVALUE
The WRITE statement will fill the
character variable CVAL with the
characters ' +98.60 ' (note that there
is one blank at each end of the
number, the first because the number
is right-justified in the field of 7
characters, the second because the
record is padded out to the declared
length of 8 characters).
Once a number has been turned into a
character-string it can be processed
further in the various ways described
in section 7. This makes it possible,
for example, to write numbers
left-justified in a field, ...
This is easier with Fortran 95, but still not trivial. Write the number or other item to a string with a write statement (as in the first answer). Then use the Fortran 95 intrinsic "ADJUSTL" to left adjust the non-blank characters of the string.
And really un-elegant is my method (I program like a cave woman), after writing the simple Fortran write format (which is not LJ), I use a combination of Excel (csv) and ultraedit to remove the spaces effectively getting the desired LJ followed directly by commas (which I need for my specific import format to another software). BF
If what you really want is whitespace between output fields rather than left-justified numbers to leave whitespace you could simply use the X edit descriptor. For example
format(A20,4X,ES18.8,4X,A12,4X,ES18.8)
will insert 4 spaces between each field and the next. Note that the standard requires 1X for one space, some of the current compilers accept the non-standard X too.
!for left-justified float with 1 decimal.. the number to the right of the decimal is how many decimals are required. Write rounds to the desired decimal space automatically, rather than truncating.
write(*, ['(f0.1)']) RValue !or
write(*, '(f0.1)') RValue
!for left-justified integers..
write(*, ['(i0)']) intValue !or
write(*, '(i0)') RValue
*after feedback from Vladimir, retesting proved the command works with or without the array brackets