Format: add trailing spaces to character output to left-justify - fortran

How do you format a string to have constant width and be left-justified? There is the Aw formatter, where w denotes desired width of character output, but it prepends the spaces if w > len(characters), instead of appending them.
When I try
44 format(A15)
print 44, 'Hi Stack Overflow'
I get
> Hi Stack Overflow<
instead of
>Hi Stack Overflow <
Is there any simple Fortran formatting solution that solves this?

As noted in the question, the problem is that when a character expression of length shorter than the output field width the padding spaces appear before the character expression. What we want is for the padding spaces to come after our desired string.
There isn't a simple formatting solution, in the sense of a natural edit descriptor. However, what we can do is output an expression with sufficient trailing spaces (which count towards the length).
For example:
print '(A50)', 'Hello'//REPEAT(' ',50)
or
character(50) :: hello='Hello'
print '(A50)', hello
or even
print '(A50)', [character(50) :: 'hello']
That is, in each case the output item is a character of length (at least) 50. Each will be padded on the right with blanks.
If you chose, you could even make a function which returns the extended (left-justified) expression:
print '(A50)', right_pad('Hello')
where the function is left as an exercise for the reader.

To complete #francescalus excellent answer for future reference, the proposed solution also works in case of allocatables in place of string literals:
character(len=:), allocatable :: a_str
a_str = "foobar"
write (*,"(A,I4)") a_str, 42
write (*,"(A,I4)") [character(len=20) :: a_str], 42
will output
foobar 42
foobar 42

a bit ugly but you can concatenate a blank string:
character*15 :: blank=' '
print 44, 'Hi Stack Overflow'//blank

program test ! Write left justified constant width character columns
! declare some strings.
character(len=32) :: str1,str2,str3,str4,str5,str6
! define the string values.
str1 = " Nina "; str2 = " Alba " ; str3 = " blue "
str4 = " Jamil "; str5 = " Arnost " ; str6 = " green "
write(*,'(a)') "123456789012345678901234567890"
! format to 3 columns 10 character wide each.
! and adjust the stings to the left.
write(*,'(3(a10))') adjustl(str1), adjustl(str2), adjustl(str3)
write(*,'(3(a10))') adjustl(str4), adjustl(str5), adjustl(str6)
end program test
$ ./a.out
123456789012345678901234567890
Nina Alba blue
Jamil Arnost green
adjustl() moves leading spaces to the end of the string.

I suggest that you do not limit the number of output characters.
Change it to the following will work:
44 format(A)
print 44, 'Hi Stack Overflow'

Related

How do I repeat one string to match the length of another string?

I am trying to repeat the string "k" to match the length of "text" without going over the length. So it would output "treetreetreetreetreet" and not "treetreetreetreetreetree". I really dont know where to start other than just outputing more characters than needed.
PROGRAM test
IMPLICIT NONE
CHARACTER*30 :: text, k
INTEGER :: times
text = 'hello my name is anon'
k = 'tree'
times = (LEN_TRIM(text)/LEN_TRIM(k)) + 1
WRITE(*,*) REPEAT(k,times)
END PROGRAM test
First, your sample program as-is doesn't produce treetreetreetreetreetree as you expect, it actually produces tree tree tree .... When you pass the string k to REPEAT, the spaces after tree also get repeated. You should trim the string before repeating it, such as REPEAT(trim(k),times).
There are several ways to solve your main problem - I recommend using what you have so far but reducing the final result to the length you want - in this case LEN_TRIM(text). A good way to do this is to store the output of REPEAT in a temporary variable and output only a subset of this final string.
With both of these modifications and some other cleanup, your code looks like:
program main
implicit none
character(len=30) :: text, k, str
integer :: times
text = 'hello my name is anon'
k = 'tree'
times = (LEN_TRIM(text)/LEN_TRIM(k)) + 1 ! -- Note integer division
str = REPEAT(trim(k),times)
write(*,*) str(1:LEN_TRIM(text))
end program main
which gives the desired output
> gfortran main.f90 && ./a.out
treetreetreetreetreet

Fortran - String with unknown characters into substrings

I am trying to put an input string into sub-string arrays. The number of data in the input file are less than 10 but unknown. The number of spaces between each data is also unclear.
Example:
Asd B Cwqe21 Ddsw Eww
I am quite novice to Fortran, so I do not know which format I should use. My problem is that I do not know the number of data (here I assumed that there are 5), so how can I make the code work?
I tried the following which did not work:
CHARACTER (LEN=100), DIMENSION(10) :: string
READ (1,*) (string,I=1,10)
It seems that the error I got was because there was no 6th string to read and put into string(6).
I tried using the "Index" to find the space, but since I do not know how many spaces are in the string, it did not help me.
I don't know if this is more or less elegant/efficient than the standard approach in M.S.B's comment, but an interesting alternative.
integer istart,nw
character (len=100) line,wd,words(100)
open(1,file='t.dat')
read(1,'(a)')line
istart=1
nw=0
do while(len(trim(line(istart:))).gt.0)
read(line(istart:),*)wd
istart=istart+index(line(istart:),trim(wd))+len(trim(wd))
nw=nw+1
words(nw)=trim(wd)
enddo
write(*,*)trim(line)
write(*,*)('/',trim(words(k)),k=1,nw),'/'
end
An inefficient approach that is simple to program is to try to read the maximum number of items, and if this fails to successively try to read one fewer items until the read is successful, as shown below:
program xread_strings
integer, parameter :: nw = 10
character (len=1000) :: text
character (len=20) :: words(nw)
integer :: i,ierr,nread
text = "Asd B Cwqe21 Ddsw Eww"
nread = 0
do i=nw,1,-1
read (text,*,iostat=ierr) words(:i)
if (ierr == 0) then
nread = i
exit
end if
end do
if (nread > 0) write (*,*) "read ",nread," words: ",("'"//trim(words(i)) // "' ",i=1,nread)
end program xread_strings
! g95 Output:
! read 5 words: 'Asd' 'B' 'Cwqe21' 'Ddsw' 'Eww'

reading delimiter with fortran

I need to read this kind of file. I have problem reading the delimiters within the file.
xxxx
dd/mm/yyyy
text
text
angle
Number of points
-13.82|654781.695|292510.337|4.889|SD
0.00|654795.515|292510.337|4.373|P1
1.25|654796.765|292510.337|4.324|SD
1.29|654796.805|292510.337|4.657|SD
1.68|654797.195|292510.337|4.622|SD
......
(1) Read lines from the file into a string using the "(a)" format.
(2) Replace unwanted delimiters in the string with delimiters recognized by Fortran (spaces or commas).
(3) Get data from the string using an "internal read".
The program below illustrates steps (2) and (3).
program main
implicit none
character (len=20) :: str
integer :: i
real :: x,y
str = "321.1|5678.9"
do i=1,len_trim(str)
if (str(i:i) == "|") str(i:i) = " "
end do
print*,"str = '" // trim(str) // "'" ! output: '321.1 5678.9'
read (str,*) x,y
print*,"x, y =",x,y ! output: x, y = 321.1 5678.9
end program main

how to replace a value of a number in a text file with another using fortran

I have a text file in which I want to replace my temperature values. The position of this numbers are random in the text and looks like:
'xdd "D:\Data\Ioana\DH1_Short_slow\DH1_360_2.xye" xye_format
local !Temperature 360
... (more text)
prm bnonh360 1.66237`_0.41541
prm bh360 = 1.2 * bnonh360;
site Na1 x ref_flag -0.11868`_0.00258 y ref_flag 0.51229`_0.00446 z ref_flag 0.00330`_0.00107 occ Na 1 beq = bnonh360;
...(more text)
Out_CIF_STR_Uiso("D:\Data\Ioana\DH1_Short_slow\DH1_360.cif")
Out_Profile("D:\Data\Ioana\DH1_Short_slow\DH1_360_plot.pro")
Out_Tick("D:\Data\Ioana\DH1_Short_slow\DH1_360_plot.tic") '
More text means more text in the file. I want to replace 360 with 365 for instance.
I have tried something like
do
read(10,'(a)',iostat=iok) line1
found = ( (index(line1,'360') /=0) )
if (found) then
write(*,*) '365'
endif
if(line1 == '$') exit
write(*,*) line1
write(40,*,iostat=iok) line1
enddo
But this just writes 365 in a next line where 360 is found.
Thanks
you are discarding valuable information in the return value of index intrinsic. to replace '360' to '365', which are of equal length, you can do
pos=index(line1,'360')
if(pos>0) line1(pos:pos+2)='365'
or in general if you want to replace first occurrence of substring s1 with s2 in a string line, with s1 and s2 possibly having different length, and that they are padded with an indefinite number of spaces that you did not want to include in the replacement, you do
pos=index(line,trim(s1))
if(pos>0) line=line(1:pos-1)//trim(s2)//line(pos+len_trim(s1):len_trim(line))
Should you want to include padding space in the replacement you can change the position offsets in the RHS substrings. If you want to perform replacements for all occurrences you can enclose this in a while loop
This is of course under the assumption that the declared length of line is long enough to hold the new string. However, note that self-assignment is prohibited in FORTRAN77 so if that is what you are using, you will need different string variable to store the result.

Fortran: Integer to String Example

I'm trying to write a simple Fortran code, for practicing. It is supposed to multiply numbers in a range. Each time, the resulting product is converted into a string because I want to see if it consists of the same digits.
I tested the way I transform an integer into a string and typed the components of the string, and everything was going correctly. Then, I need to compare the components of the string, for which I use string(number:number). But I couldn't get the code to do this correctly.
Here's the code and the output:
program test
implicit none
character(10) myString
character(1) a,b,c,d,e,f
integer::i,j,k
do i=900,901,1
j=900
k=i*j
write(*,*)'k =', k
write(myString,'(i10)') k
write(*,*)'myString = ', myString
a=myString(1:1)
b=myString(2:2)
c=myString(3:3)
d=myString(4:4)
e=myString(5:5)
f=myString(6:6)
print*,a,b,c,d,e,f
if (d==f) then
print*,'hobla'
else
print*,'fobla'
end if
end do
stop
end program test
So I defined characters: a,b,c,d,e,f to contain the components of the string. And used myString(i:i) to locate each component and store it in one of the characters a,b,c,d,e,f.
But it seems only the first two are working correctly, the rest is not being stored!
Output:
k = 810000
myString = 810000
81
fobla
k = 810900
myString = 810900
81
fobla
Notice 81. This was supposed to give 810000 the first time, and print "hobla". And give 810900 the second time and print "fobla". But this didn't happen!
Can anybody show me how to let myString accept the zeros as characters?
This statement
write(myString,'(i10)') k
writes the value of k into a 10-character field. Since k has only 6 significant digits the first 4 characters in myString are filled with blanks. Then you assign the first 6 characters of myString (that is 4 blanks and the digits 8 and 1) to the variables a,b,c,d,e,f and print them out -- 4 blanks and 2 digits.
Try printing out the other characters in positions 7..10 of myString and you should see your 'missing' digits.
Okay, so I figured out the problem. Consider the following modifications to your code:
program test
implicit none
character(10) myString
character(1) a,b,c,d,e,f
integer::i,j,k
do i=900,901,1
j=900
k=i*j
write(*,*)'k =', k
write(myString,'(i10)') k
write(*,*)'myString = ', myString
a=myString(1:1)
b=myString(2:2)
c=myString(3:3)
d=myString(4:4)
e=myString(5:5)
f=myString(6:6)
write(*,*) a
write(*,*) b
write(*,*) c
write(*,*) d
write(*,*) e
write(*,*) f
if (d==f) then
print*,'hobla'
else
print*,'fobla'
end if
end do
stop
end program test
The resulting output is:
k = 810000
myString = 810000
8
1
fobla
k = 810900
myString = 810900
8
1
fobla
This happens because the I format right-justifies numbers when printed. So there is a bunch of leading white-spaces because your number is only 6 digits while your string you are writing into is 10. So you get 4 leading spaces.
If you change your format string so you have:
write(myString,'(i10.10)') k
then instead of padding with spaces it will pad with 0's. That way you can always have digits to compare if you would rather.