58

I have a program in Fortran that saves the results to a file. At the moment I open the file using

OPEN (1, FILE = 'Output.TXT')

However, I now want to run a loop, and save the results of each iteration to the files 'Output1.TXT', 'Output2.TXT', 'Output3.TXT', and so on.

Is there an easy way in Fortran to constuct filenames from the loop counter i?

Alasdair
  • 298,606
  • 55
  • 578
  • 516

10 Answers10

62

you can write to a unit, but you can also write to a string

program foo
    character(len=1024) :: filename

    write (filename, "(A5,I2)") "hello", 10

    print *, trim(filename)
end program

Please note (this is the second trick I was talking about) that you can also build a format string programmatically.

program foo

    character(len=1024) :: filename
    character(len=1024) :: format_string
    integer :: i

    do i=1, 10
        if (i < 10) then
            format_string = "(A5,I1)"
        else
            format_string = "(A5,I2)"
        endif

        write (filename,format_string) "hello", i
        print *, trim(filename)
    enddo

end program
Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
  • 23
    Two comments: - you don't have to discriminate on the value of I; the format (I0) will output an integer without any space; also, if you want a fixed width and padding with zeroes (like "output001.txt"), you need to used (I0.3) - the format (A5I2) is not valid Fortran according to any norm, as format specifiers are to be separated by commas: (A5,I2) – F'x Aug 12 '09 at 14:47
  • Well, it was for educational purposes, not intended to be the solution. In general I use the padding zeros (as it sorts nicely), but the I0 thingie I didn't know about. Thanks!! (fixed the commas, I think my style was the old one, still accepted) – Stefano Borini Aug 12 '09 at 15:53
  • @F'x thanks for the comment, really useful. Indeed even trim won't work if the number k of digits is not equal to "(Ik)" in the format, so just use "(I0)" so that one doesn't need to adapt the format. – gluuke Jan 16 '14 at 16:57
  • There is not A0 as the counterpart of I0, right? (meaning a character string with any padding) – Manuel Pena Nov 08 '18 at 17:11
  • 1
    @ManuelPena *"w shall be zero or positive for the I, B, O, Z, D, E, EN, ES, EX, F, and G edit descriptors. **w shall be positive for all other edit descriptors**."* (Fortran 2018, where *w* is the width parameter). However, the width is not mandatory for the A edit descriptor, and if omitted, the length of the character string is used instead. But bear in mind that this length includes any trailing space: if you want to remove them from the output, use the A edit descriptor (without length parameter) **and** pass `trim(s)` instead of `s`. You may also want to `adjustl`. –  Aug 31 '20 at 09:20
22

A much easier solution IMHO ...................

character(len=8) :: fmt ! format descriptor

fmt = '(I5.5)' ! an integer of width 5 with zeros at the left

i1= 59

write (x1,fmt) i1 ! converting integer to string using a 'internal file'

filename='output'//trim(x1)//'.dat'

! ====> filename: output00059.dat
ocodo
  • 29,401
  • 18
  • 105
  • 117
Alejandro
  • 221
  • 2
  • 2
  • 1
    may just be my compiler, but i was needed to declare a character variable for the output string written to (i.e. `character(5) x1`). thanks! – ryanjdillon Mar 05 '14 at 20:13
15

Well here is a simple function which will return the left justified string version of an integer:

character(len=20) function str(k)
!   "Convert an integer to string."
    integer, intent(in) :: k
    write (str, *) k
    str = adjustl(str)
end function str

And here is a test code:

program x
integer :: i
do i=1, 100
    open(11, file='Output'//trim(str(i))//'.txt')
    write (11, *) i
    close (11)
end do
end program x
cyberthanasis
  • 209
  • 2
  • 4
8

I already showed this elsewhere on SO (How to use a variable in the format specifier statement? , not an exact duplicate IMHO), but I think it is worthwhile to place it here. It is possible to use the techniques from other answers for this question to make a simple function

function itoa(i) result(res)
  character(:),allocatable :: res
  integer,intent(in) :: i
  character(range(i)+2) :: tmp
  write(tmp,'(i0)') i
  res = trim(tmp)
end function

which you can use after without worrying about trimming and left-adjusting and without writing to a temporary variable:

OPEN(1, FILE = 'Output'//itoa(i)//'.TXT')

It requires Fortran 2003 because of the allocatable string.

Community
  • 1
  • 1
3

For a shorten version. If all the indices are smaller than 10, then use the following:

do i=0,9
   fid=100+i
   fname='OUTPUT'//NCHAR(i+48) //'.txt'
   open(fid, file=fname)
   !....
end do

For a general version:

character(len=5) :: charI
do i = 0,100
   fid = 100 + i
   write(charI,"(A)"), i
   fname ='OUTPUT' // trim(charI) // '.txt'
   open(fid, file=fname)
end do

That's all.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
kayneo
  • 67
  • 4
  • There should be no comma after after the closing parentheses in write statement. (( write(charI,"(A)") i )) Thanks, I am using this suggestion. – Osman Mamun Jul 13 '15 at 17:50
  • The default is no comma, but I found it works well if the additional comma added, similar as ((print *, i)) – kayneo Sep 13 '16 at 02:00
0

I've tried @Alejandro and @user2361779 already but it gives me an unsatisfied result such as file 1.txt or file1 .txt instead of file1.txt. However i find the better solution:

...
integer :: i
character(len=5) :: char_i     ! use your maximum expected len
character(len=32) :: filename

write(char_i, '(I5)') i        ! convert integer to char
write(filename, '("path/to/file/", A, ".dat")') trim(adjustl(char_i))
...

Explanation:

e.g. set i = 10 and write(char_i, '(I5)') i

char_i                gives  "   10" ! this is original value of char_i

adjustl(char_i)       gives  "10   " ! adjust char_i to the left

trim(adjustl(char_i)) gives  "10"    ! adjust char_i to the left then remove blank space on the right

I think this is a simplest solution that give you a dynamical length filename without any legacy blank spaces from integer to string conversion process.

fronthem
  • 4,011
  • 8
  • 34
  • 55
  • 1
    Using `I0` as the edit descriptor would be much simpler: `write(filename, '("path/to/file/", I0, ".dat")') i`. This is mentioned previously in this question only in comments, so perhaps could be added. – francescalus Jun 24 '15 at 12:41
  • Thank you @francescalus, I didn't notice that comment. Then left my answer to be an alternative solution. it may be useful in some case. – fronthem Jun 24 '15 at 12:50
0

Try the following:

    ....
    character(len=30) :: filename  ! length depends on expected names
    integer           :: inuit
    ....
    do i=1,n
        write(filename,'("output",i0,".txt")') i
        open(newunit=iunit,file=filename,...)
        ....
        close(iunit)
    enddo
    ....

Where "..." means other appropriate code for your purpose.

sɐunıɔןɐqɐp
  • 3,332
  • 15
  • 36
  • 40
Michael
  • 42
  • 3
0

Here is the answer for writing files for different parameters in a loop

program sinnx_diff_n
implicit none
integer :: n, xloop
real*8  :: x,y
character(len=40)  :: nchar

do n=1,4
    
    write(nchar,6)n
    6 format(I5)    

    open (8,file="data-x-vs-y-n="//trim(adjustl(nchar))//"-values.dat", status="replace",action="write")   ! this line is storing data for each n value.

    do xloop=-40,40
        x=real(xloop)/10.0d0
        y=sin(n*x)            ! writing y = sin(x) for a single n value
        write(8,34)x,y
        34 format(2x, 10f16.8)  ! this will accommodate 10 columns of real values with gap of 2 spaces between each data column.
    end do            ! end of x loop

end do  ! end of different n loop   
    
end program
LinFelix
  • 1,026
  • 1
  • 13
  • 23
-2

To convert an integer to a string:

integer :: i    
character* :: s    
if (i.LE.9) then
     s=char(48+i)    
else if (i.GE.10) then
     s=char(48+(i/10))// char(48-10*(i/10)+i)    
endif
francescalus
  • 30,576
  • 16
  • 61
  • 96
-3

Here is my subroutine approach to this problem. it transforms an integer in the range 0 : 9999 as a character. For example, the INTEGER 123 is transformed into the character 0123. hope it helps.

P.S. - sorry for the comments; they make sense in Romanian :P

 subroutine nume_fisier (i,filename_tot)

   implicit none
   integer :: i

   integer :: integer_zeci,rest_zeci,integer_sute,rest_sute,integer_mii,rest_mii
   character(1) :: filename1,filename2,filename3,filename4
   character(4) :: filename_tot

! Subrutina ce transforma un INTEGER de la 0 la 9999 in o serie de CARACTERE cu acelasi numar

! pentru a fi folosite in numerotarea si denumirea fisierelor de rezultate.

 if(i<=9) then

  filename1=char(48+0)
  filename2=char(48+0)
  filename3=char(48+0)
  filename4=char(48+i)  

 elseif(i>=10.and.i<=99) then

  integer_zeci=int(i/10)
  rest_zeci=mod(i,10)
  filename1=char(48+0)
  filename2=char(48+0)
  filename3=char(48+integer_zeci)
  filename4=char(48+rest_zeci)

 elseif(i>=100.and.i<=999) then

  integer_sute=int(i/100)
  rest_sute=mod(i,100)
  integer_zeci=int(rest_sute/10)
  rest_zeci=mod(rest_sute,10)
  filename1=char(48+0)
  filename2=char(48+integer_sute)
  filename3=char(48+integer_zeci)
  filename4=char(48+rest_zeci)

 elseif(i>=1000.and.i<=9999) then

  integer_mii=int(i/1000)
  rest_mii=mod(i,1000)
  integer_sute=int(rest_mii/100)
  rest_sute=mod(rest_mii,100)
  integer_zeci=int(rest_sute/10)
  rest_zeci=mod(rest_sute,10)
  filename1=char(48+integer_mii)
  filename2=char(48+integer_sute)
  filename3=char(48+integer_zeci) 
  filename4=char(48+rest_zeci)

 endif

 filename_tot=''//filename1//''//filename2//''//filename3//''//filename4//''
 return
 end subroutine nume_fisier
wallyk
  • 56,922
  • 16
  • 83
  • 148
  • 7
    This is a very bad answer. As the accepted answer already shows Fortran provides a mechanism for writing the value of an integer into a character variable; all this fiddling around with encoding and decoding character indices is a horrid hack which serves no useful purpose. – High Performance Mark Jun 28 '12 at 08:59
  • 4
    @HighPerformanceMark Regarding his name, this code looks quite reasonable. – PVitt Apr 13 '16 at 10:41