2

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]

5
  • Welcome. Use tag fortran for all Fortran questions. You can add a version tag to request a specific version, but note that Fortran 90 is very old and many features reuquire more recent versions. Be sure to take the tour and read How to Ask to find out more how to use this site. Commented Sep 22, 2017 at 20:42
  • It would be helpful to see the original Intel Fortran format you are porting. Commented Sep 22, 2017 at 20:42
  • the advance='no' approach is probably as clean as anything else. Commented Sep 23, 2017 at 2:07
  • a bit aside and i can't test it, but does using y both as the iterator and the bound: (y,y=X,Y) really work? Even so I'd introduce a different iterator symbol just for readability. Commented Sep 23, 2017 at 2:14
  • Agentp - in the interest of avoiding spaghettification I replaced some wordy expressions with "X" and "Y" in my presentation. "y" was the original iterator, however. I was under the impression that "y" and "Y" weren't the same variable. Commented Sep 25, 2017 at 12:49

3 Answers 3

5

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!

Sign up to request clarification or add additional context in comments.

3 Comments

It could be good to point out explicitly the use of G0, as that's the important point! [Also, this is limited to those cases where leading spaces in the trailing string are not important.]
Yes, I should have done that and now have. I don't understand your comment about leading spaces - I think that is entirely under the programmer's control. Even the additional spaces after STRING1 can be minimized with some rearranging of the format.
Well, dang, that G0 sure beats doing internal writes.
1
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

2 Comments

A brief explanation as well as the code snippet would probably be useful as well.
+the trim is unnecessary by the way. the extra blanks in the format string wont hurt anything. I'd do I9 instead of 5x,i4 as well..tighten up the code.
1

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.

2 Comments

I have heard from several compiler developers that they rue the day that DEC invented Variable Format Expressions (back in the PDP-11 days), as it is so unlike the way Fortran worked (at least until UDDTIO, which it resembles somewhat.) It's tricky to get scoping right when you have to create this tiny coroutine that gets invoked by the I/O library. The standards committee created unlimited repeat count and G0 as an alternative that works for the majority of cases.
@SteveLionel Nice comment! Yeah, I guess I've heard/read that variable format is pretty unloved, and I guess I can understand. Still, pretty handy when you don't care about portability (Shock! Gasp!). Anyway, I tend to use *, and if that doesn't work I just decide to rearrange my output so that it does...I should look into G.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.