I need a portable function/subroutine to locate the position of the last non-blank character in a string. I've found two options: LEN_TRIM and LNBLNK. However, different compilers seem to have different standards. The official documentation for the following compilers suggests that LEN_TRIM is part of the Fortran 95 standard on the following platforms:
IBM: LEN_TRIM
Intel: LNBLNK and LEN_TRIM
gfortran: LNBLNK and LEN_TRIM
PGI: LEN_TRIM
However, it appears that nothing is guaranteed on compilers released before the F95 standard. My experience has been that older compilers might specify either LEN_TRIM or LNBLNK, but not necessarily both. My solution has been to use preprocessor conditionals:
#ifdef USE_LEN_TRIM
iNameLen = LEN_TRIM(cabase)
iExtLen = LEN_TRIM(caext)
#else
iNameLen = LNBLNK(cabase)
iExtLen = LNBLNK(caext)
#endif
and then pass -DUSE_LEN_TRIM to the preprocessor. However, I am not a big fan of preprocessor conditionals & extra compile-time flags. Do you have any suggestions for a portable (before the Fortran 95 standard) function that locate the position of the last non-blank character in a string?
LEN_TRIM is part of the Fortran 95 standard. I am not aware of any simple combination of intrinsics in Fortran 77 that could tackle this (TRIM also appeared with Fortran 95, and wasn't available in Fortran 77). But you can use a simple user function to perform that task if you really want to be portable without relying on compiler intrinsics and preprocessor:
function lentrim(s)
implicit none
character(len=*) :: s
integer lentrim
do lentrim = len(s), 1, -1
if (s(lentrim:lentrim) .ne. ' ') return
end do
end function lentrim
(tested with g77 and gfortran; use option -ffree-form to compile free-form source like this).
Before the standard, I would say no. I've seen the len_trim function implemented by hand on F77 codes. If you want to be standard toward old compilers, then you will have to provide your own implementation as a failsafe, and use a preprocessor conditional to pick what you need.
I'm stuck trying to find if this is a pre-Fortran 95 or not, but does
len(trim(s))
work?
Related
I have a project written in VS2010 with Intel Visual Fortran. I have a dump subroutine to write a 2D matrix into file:
subroutine Dump2D(Name,Nx,Ny,Res)
implicit none
integer i,j,Nx,Ny
real(8) :: Res(Nx,Ny)
character(len=30) name,Filename
logical alive
write(filename,*) trim(Name),".dat"
Write(*,*) "Saving ",trim(Name)," Please wait..."
open (10,file=filename)
do i=1,Ny
Write(10,FMt="(D21.13\)") (Res(j,i),j=1,Nx)
Write(10,*)
end do
close(10)
Write(*,*) "Save ",trim(Name),"Complete!"
return
end subroutine Dump2D
It is ok to compile and run. But when I compile in emacs using gfortran it gives me the error:
I think it's because the gfortran doesn't recognize \ in a format for a write command. How do I fix this problem?
Write(10,FMt="(D21.13\)") (Res(j,i),j=1,Nx)
1
Error: Unexpected element '\' in format string at (1)
The edit descriptor \ relates to backslash editing. This is a non-standard extension provided by the Intel compiler (and perhaps others). It is not supported by gfortran.
Such backslash editing is intended to affect carriage control. Much as in this answer such an effect can be handled with the (standard) non-advancing output.1
As you simply want to output each column of a matrix to a record/line you needn't bother with this effort.2 Instead (as you'll see in many other questions):
do i=1,Ny
write(10,fmt="(*(D21.13))") Res(:,i)
end do
There are also other approaches which a more general search will find.
1 The Intel compiler treats \ and $ in the same way.
2 There are subtle aspects of \, but I'll assume you don't care about those.
Another approach (although francescalus answer is better in your case) would be to build a format string that contains the number of elements to include in your row. One way of doing this is to use the following to build the format string (which uses an explicit space character to separate elements within a line in the file):
WRITE(fmtString, '(A,I0,A)') '(', Nx, '(D21.13,:,1X))' *
Then use the format string variable in your WRITE statement as so:
do i=1,Ny
Write(10,FMt=fmtString) (Res(j,i),j=1,Nx)
end do
This approach can also be very useful if you want to use something other than spaces to separate elements (e.g. commas or semicolons).
*As that's a little difficult to read, I will provide an example. For Nx = 3, this would be equivalent to:
fmtString = '(3(D21.13,:,1X))'
Which is 2 numbers formatted using D21.13, each followed by a space, and a final number formatted using D21.13, but without a space after it (as the ":" stops at the final item).
The backslash is not valid in Fortran 77 FORMAT statements. Gfortran will not compile it, unless you fix the code. There is no flag that will change that AFAIK (-fbackslash should not help here).
If I understand the intention correctly (and I may be wrong), the backslash does the same as the dollar sign in some other compilers and prevents terminating a record (line). In that case the advance="no" put in the write statement should help. It is Fortran 90, but you should not avoid it just for that reason.
I have a project written in VS2010 with Intel Visual Fortran. I have a dump subroutine to write a 2D matrix into file:
subroutine Dump2D(Name,Nx,Ny,Res)
implicit none
integer i,j,Nx,Ny
real(8) :: Res(Nx,Ny)
character(len=30) name,Filename
logical alive
write(filename,*) trim(Name),".dat"
Write(*,*) "Saving ",trim(Name)," Please wait..."
open (10,file=filename)
do i=1,Ny
Write(10,FMt="(D21.13\)") (Res(j,i),j=1,Nx)
Write(10,*)
end do
close(10)
Write(*,*) "Save ",trim(Name),"Complete!"
return
end subroutine Dump2D
It is ok to compile and run. But when I compile in emacs using gfortran it gives me the error:
I think it's because the gfortran doesn't recognize \ in a format for a write command. How do I fix this problem?
Write(10,FMt="(D21.13\)") (Res(j,i),j=1,Nx)
1
Error: Unexpected element '\' in format string at (1)
The edit descriptor \ relates to backslash editing. This is a non-standard extension provided by the Intel compiler (and perhaps others). It is not supported by gfortran.
Such backslash editing is intended to affect carriage control. Much as in this answer such an effect can be handled with the (standard) non-advancing output.1
As you simply want to output each column of a matrix to a record/line you needn't bother with this effort.2 Instead (as you'll see in many other questions):
do i=1,Ny
write(10,fmt="(*(D21.13))") Res(:,i)
end do
There are also other approaches which a more general search will find.
1 The Intel compiler treats \ and $ in the same way.
2 There are subtle aspects of \, but I'll assume you don't care about those.
Another approach (although francescalus answer is better in your case) would be to build a format string that contains the number of elements to include in your row. One way of doing this is to use the following to build the format string (which uses an explicit space character to separate elements within a line in the file):
WRITE(fmtString, '(A,I0,A)') '(', Nx, '(D21.13,:,1X))' *
Then use the format string variable in your WRITE statement as so:
do i=1,Ny
Write(10,FMt=fmtString) (Res(j,i),j=1,Nx)
end do
This approach can also be very useful if you want to use something other than spaces to separate elements (e.g. commas or semicolons).
*As that's a little difficult to read, I will provide an example. For Nx = 3, this would be equivalent to:
fmtString = '(3(D21.13,:,1X))'
Which is 2 numbers formatted using D21.13, each followed by a space, and a final number formatted using D21.13, but without a space after it (as the ":" stops at the final item).
The backslash is not valid in Fortran 77 FORMAT statements. Gfortran will not compile it, unless you fix the code. There is no flag that will change that AFAIK (-fbackslash should not help here).
If I understand the intention correctly (and I may be wrong), the backslash does the same as the dollar sign in some other compilers and prevents terminating a record (line). In that case the advance="no" put in the write statement should help. It is Fortran 90, but you should not avoid it just for that reason.
I'm attempting to modernize an old code (or at least make it a bit more understandable) but I've run into an odd format for a, uh, FORMAT statement.
Specifically, it's a FORMAT statement with Hollerith constants in it (the nH where n is a number):
FORMAT(15H ((C(I,J),J=1,I3,12H),(D(J),J=1,I3, 6H),I=1,I3,') te'
1,'xt' )
This messes with the syntax highlighting as it appears this has unclosed parenthesis. It compiles fine with this format statement as is, but closing the parenthesis causes a compiling error (using either the intel or gfortran compiler).
As I understand it, Hollerith constants were a creature of Fortran 66 and were replaced with the advent of the CHARACTERin Fortran 77. I generally understand them when used as something like a character, but use as a FORMAT confuses me.
Further, if I change 15H ((... to 15H ((... (i.e. I remove one space) it won't compile. In fact, it won't compile even if I change the code to this:
FORMAT(15H ((C(I,J),J=1,I3,12H),(D(J),J=1,I3, 6H),I=1,I3,') text' )
I would like this to instead be in a more normal (F77+) format. Any help is appreciated.
What you have are actually Hollerith edit descriptors, not constants (which would occur in a DATA or CALL statement), although they use the same syntax. F77 replaced Hollerith constants outright; it added char-literal edit descriptor as a (much!) better alternative, but H edit descriptor remained in the standard until F95 (and even then some compilers still accepted it as a compatibility feature).
In any case, the number before the H takes that number of characters after the H, without any other delimiter; that's why deleting (or adding) a character after the H screws it up. Parsing your format breaks it into these pieces
15H ((C(I,J),J=1,
I3,
12H),(D(J),J=1,
I3,
6H),I=1,
I3,
') te'
'xt'
and thus a modern equivalent (with optional spaces for clarity) is
nn FORMAT( ' ((C(I,J),J=1,', I3, '),(D(J),J=1,', I3, '),I=1,', I3
1,') text' )
or if you prefer you can put that text after continuation (including the parens) in a CHARACTER value, variable or parameter, used in the I/O statement instead of a FORMAT label, but since you must double all the quote characters to get them in a CHARACTER value that's less convenient.
Your all-on-one-line version probably didn't compile because you were using fixed-form, perhaps by default, and only the first 72 characters of each source line are accepted in fixed-form, of which the first 6 are reserved for statement number and continuation indicator, leaving only 66 and that statement is 71 by my count. Practically any compiler you will find today also accepts free-form, which allows longer lines and has other advantages too for new code, but may require changes in existing code, sometimes extensive changes.
I have noticed the results of list-directed output write(*,*) in Fortran is compiler dependent.
Indeed, with the code:
program one
real(8), dimension(5):: r1
do i=1,5
r1(i)=sqrt(i*10.0)
end do
write(*,*) (r1(i), i =1,5)
end program one
intel compiler ifort gives standard output broken by a newline:
3.16227769851685 4.47213602066040 5.47722530364990
6.32455539703369 7.07106781005859
while gfortran gives the equivalent one line result:
3.1622776601683795 4.4721359549995796 5.4772255750516612 6.3245553203367590 7.0710678118654755
I think that ifort is writing maximum 3 items per line (when floating real numbers).
Is there any way to make the ifort output be like gfrotran, i.e. avoid the newline?
Ideally, I would like to keep list-directed output (*,*) instructions, so I am looking for something like a compiler option or so, if any.
Since verson 14, intel fortran compiler has the wrap-margin function. By default, the record is wrapped after 80 characters. For disabling this restriction, you should specify:
on Linux: -no-wrap-margin
on WIndows: /wrap-margin-
See more on Intel Fortran's reference guide
No. List-directed (free-format) output provides convenience, but you give up control. Various aspects of the output are unspecified and allowed to be chosen to the compiler. If you want full control, you have to use formatted output.
Look into edit descriptors in your favorite Fortran book or online documentation. You can use fmt specifier in the write statement to specify edit descriptors. For example:
write(*,fmt='(5(F6.4,3X))') (r1(i), i =1,5)
should output something similar to:
3.1623 4.4721 5.4772 6.3246 7.0711
See https://software.intel.com/en-us/forums/topic/401555
Specify FORT_FMT_RECL or use
write (,"(G0,1X))"
I'm currently in the process of upgrading an old program written in FORTRAN and I'm trying to get it to compile using gfortran. I can get it to build, however the program now chokes on the namelists that it is reading in.
An example of a namelist that came with the program is shown below. It cannot correctly read this file due to the space between the variable name and the parenthesis. Since these files are very large, is there anyway I can tell gfortran to expect an older namelist format?
&<NAME>
<VARNAME> (1) = 0,
<VARNAME> (2) = -1e-13,
<VARNAME> (3) = 0.2983
&End
If not, I can resort to doing a search/replace but I'd rather not mess with the data and just have the code successfully read it in.
Namelist is quite complicated, as you can tell from the fact that there are 92 namelist test cases in the gfortran testsuite.
gfortran has no option to do what you ask. You will probably have to remove the blanks by hand or by sed, like Vladimir F suggested.
I might add that blanks in the subobject designator (the varname(i) part) are explicitly forbidden in the F2003 standard 10.10.1.1:
"In the input record, each object name or subobject designator may be preceded and followed by one or more optional blanks but shall not contain embedded blanks."
so this is unlikely to be changed in a future version of gfortran.