Error counting substrings from file in Fortran 95 - fortran

I'm trying to write a code in Fortran 95 that read strings from a file and then print a frequency table of letters from the file. The file has a text like the example below, with no more than 128 characters per line:
Ablblb lbla sdrtwfwefw
Waerfaw efeafawef awef
Pefwae fwefawefw efawe
Fcicnj ioejo, o njcdid
Pweko jai, wadwed awdd
So I did one program that is working fine for the first letter (A or a), but the counter doesn't work for the rest of the letters and always print "0". This is what I did:
program read_file
implicit none
integer :: count, i, j, k, n
character(len=80) :: arch
character(len=128) :: line
character :: c, d
logical :: rexist
print *,'Name of the file (example: "text1.txt"): '
read *,arch
count=0
inquire(file=arch, exist=rexist)
if(rexist) then
open(unit=10, file=arch)
j=97
do i=65,90
c=achar(i)
d=achar(j)
do
read(unit=10, fmt='(A128)', end=999)line
n=len_trim(line)
do k=1,n
if(line(k:k)==c .or. line(k:k)==d)then
count=count+1
end if
end do
cycle
999 exit
end do
print *, "Letter ", c, " or ", d, " Total: ", count
j=j+1
count=0
end do
close(unit=10)
else
print *,"Invalid file!"
end if
end program read_file
The first count works fine (letter A or a) but it prints "0" for the rest of the letters. Is there something wrong with the DO loop that doesn't reset the variable count properly?

Related

Removing whitespace in string

I have the following code:
program main
character (len=15) :: abc = "te st tex t"
print *, trim(abc)
end program main
Which outputs:
te st tex t
I excepted all the whitespace to be removed but it wasn't. How can I remove all the whitespace from the string?
Trim will remove spaces only at the edges, not in the middle (this is common behaviour on almost all languages/libraries). If you want to remove all spaces in the string, you will have to create your own function to do this, iterating through the string.
Ex.:
program Test
implicit none
! Variables
character(len=200) :: string
! Body of Test
string = 'Hello World 7 9'
print *, string
call StripSpaces (string)
print *, string
contains
subroutine StripSpaces(string)
character(len=*) :: string
integer :: stringLen
integer :: last, actual
stringLen = len (string)
last = 1
actual = 1
do while (actual < stringLen)
if (string(last:last) == ' ') then
actual = actual + 1
string(last:last) = string(actual:actual)
string(actual:actual) = ' '
else
last = last + 1
if (actual < last) &
actual = last
endif
end do
end subroutine
end program Test
This was tested on intel compiler, not on gfortran, but I think it will work.
I was able to do this using the variable string library described here ( http://schonfelder.co.uk/is1539-2-99.htm ). The source code link is found in the introduction section of the ISO document.
Here is the code
program Console1
use ISO_VARYING_STRING
implicit none
! Body of Console1
character(LEN=50) :: text = 'Hello World John Mary '
character(LEN=50) :: res
print *, trim(text)
! 'Hello World John Mary'
res = REPLACE(text,' ','', every=.TRUE.)
print *, trim(res)
! 'HelloWorldJohnMary'
end program Console1
Here's a dirty, shameful way to eliminate the spaces. This is only likely to work if a compiler lays out a length-15 string in the same order and space as it would a 15-element array of characters. While this is likely to be true, and in my recent experience is true, it is not guaranteed to be so by the standard. That aside, this approach may be good enough.
! declarations
CHARACTER (len=15) :: abc = "te st tex t"
CHARACTER, DIMENSION(LEN(abc)) :: abc_array
! or CHARACTER, DIMENSION(:), ALLOCATABLE :: abc_array if your compiler supports
! automatic allocation
! transfer the string into an array of characters
abc_array = TRANSFER(abc,abc_array)
! eliminate the spaces, and transfer back to the string
abc = TRANSFER(PACK(abc_array,abc_array/=' '),abc)
! now all the spaces are at the end of abc so the following statement writes the
! string with no spaces
WRITE(*,*) TRIM(abc)
Use this approach at your own risk.
For those averse to TRANSFER perhaps a nice little recursive function would appeal. As written this depends on Fortran 2003's ability to automatically allocate character scalars, but it shouldn't be too hard to modify if your compiler doesn't support this feature yet.
RECURSIVE FUNCTION stripper(string,ch) RESULT(stripped)
CHARACTER(len=*), INTENT(in) :: string
CHARACTER, INTENT(in) :: ch
CHARACTER(:), ALLOCATABLE :: stripped
IF (LEN(string)==1) THEN
IF (string==ch) THEN
stripped = ''
ELSE
stripped = string
END IF
ELSE
IF (string(1:1)==ch) THEN
stripped = stripper(string(2:),ch)
ELSE
stripped = string(1:1)//stripper(string(2:),ch)
END IF
END IF
END FUNCTION stripper
You can try this:
program test
!erase blank space in a string
!run over every character of the string and just take every non-blank in other variable.
implicit none
character (len=100) str1,str2
integer i
str2='' !in this variable will be save non-blank spaces
str1=' a b c de ' !Test string with blank spaces
write(*,*)len_trim(str1), str1
do i=1,len(str1)
if (str1(i:i).ne.' ')str2=trim(str2)//trim(str1(i:i))
end do
write(*,*)len_trim(str2), str2
end

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'

Spaces in fortran string output

I have the following Fortran 95 code:
program write_test
CHARACTER(LEN=3) :: str
write(*,*) (' ',"""",'File'//trim(str(i)),"""",' ',"""",'Frequency'//trim(str(i)),"""",i=1,5)
end program write_test
!character*(*) function str(k)
character(*) function str(k)
! "Convert an integer to string."
integer, intent(in) :: k
write (str, *) k
str = adjustl(str)
end function str
When I compile and run it, I get the following output:
" File1" " Frequency1" " File2" " Frequency2" " File3" " Frequency3" " File4" " Frequency4" " File5" " Frequency5"
Why is there a space between the double quote and the letter F?
For list-directed output with the * "format", the compiler has freedom to insert one or more spaces between the items printed. For more precise control, use a format string, as in the code below, tested with g95 and gfortran. You can use the i0 format to print an integer without spaces.
program write_test
character(len=3) :: str
write (*,"(100a)") (" ","""","File"//trim(str(i)),"""", &
" ","""","Frequency"//trim(str(i)),"""",i=1,5)
! preferred way is line below
write (*,"(100(1x,2a,i0,a))") ("""","File",i,"""","""","Frequency",i,"""",i=1,5)
end program write_test
character(*) function str(k)
! Convert an integer to string
integer, intent(in) :: k
write (str,"(i0)") k
str = adjustl(str)
end function str

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

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.