check if list/array of strings contains string [duplicate] - fortran

This question already has answers here:
Compact if check involving multiple strings in Fortran [duplicate]
(1 answer)
gfortran does not allow character arrays with varying component lengths
(2 answers)
Different CHARACTER lengths (3/4) in array constructor, how to trim strings - fortran
(1 answer)
Closed 5 years ago.
I would like to check if a given string is contained in a list/array of strings. In python I would do something like this:
test_string = 'alpha'
if test_string in ['alpha', 'beta', 'gamma', 'delta']:
print('found it')
else:
print('it's not there')
A simple FORTRAN program mimicking this obviously doesn't work:
PROGRAM STRINGCHECK
IMPLICIT NONE
CHARACTER(len = 10) :: test_string
CHARACTER(len = 10), DIMENSION(4) :: string_array
test_string='alpha'
string_array = ['alpha', 'beta', 'gamma', 'delta']
IF (ANY( string_array == test_string)) THEN
WRITE(*,*) 'found it'
END IF
END PROGRAM
In fact, it already fails at the array assignment with
Error: Different CHARACTER lengths (5/4) in array constructor at (1)
Is there really no other way than to check every string explicitly?:
IF ((test_string == 'alpha') .or. (test_string == 'beta') .or. &
(test_string == 'gamma') .or. (test_string == 'delta')) THEN
WRITE(*,*) 'found it'
END IF
UPDATE
The commenters are indeed right, changing the construction of the array makes the string comparison work just as expected:
CHARACTER(len = 10) :: test_string
CHARACTER(len = 10), DIMENSION(4) :: string_array
test_string='alpha'
string_array(1) = 'alpha'
string_array(2) = 'beta'
string_array(2) = 'gamma'
string_array(2) = 'delta'
IF (ANY(string_array == test_string)) THEN
WRITE(*,*) 'found it'
END IF
However, constructing the array in this way somewhat defeats the purpose of this undertaking, which would be to define the array of strings inside the IF statement, just like in my example python code.
UPDATE #2
Thanks to High Performance Mark, the answer to my problem was as easy as padding
all strings inside the array with whitespaces to make them the same length (EDIT: I replaced the [...] with (/.../) according to VladimirF's commend):
IF (ANY((/ 'alpha', 'beta ', 'gamma', 'delta'/) == 'beta')) THEN
WRITE(*,*) 'found it'
END IF
This is distinctly different from python, where the whitespace would make the two strings differ.

Related

Combining strings and integers to make a variable format: Missing initial left parenthesis in format (Fortran)

I want to make a variable format for my "write" statement. To do so, I wrote a small program (nvari is the variable):
program VariableFormat
implicit none
integer :: x = 1,y = 2, z = 3, i, nvari
double precision :: pi = 3.14
integer, allocatable :: var(:)
integer :: A(3) = (/1,2,3/)
character(100) :: fmt,fmt2,str1,str2,str3
print*, size(A)
allocate(var(size(A)))
do i = 1, size(A)
var(i) = A(i)
end do
nvari = 2
!
! first part
!
fmt = '(a,f4.2)'
write(*,fmt) "The value of pi is ", pi
!
! second part
!
write (str1, "(1A2,1I1,1A1,1I2,1A1)") "'(", 3, "I", 15, ","
print*, str1
write(str2,'(I10)') nvari
print*, str2
write (str3, "(1A1,1I2,1A2)") "I", 15, ")'"
print*, str3
fmt2 = trim(adjustl(str1))//trim(adjustl(str2))//trim(adjustl(str3))
print*, fmt2
write(*,fmt2) x,y,z,(var(i),i=1,nvari)
end program VariableFormat
First question:
It can be seen that fmt2 is similar to fmt in the sense that in both we have '(X,Y)'.
However, while the first part of the code is executed correctly, for the second part, I have the error:
At line 32 of file VariableFormat.F90 (unit = 6, file = 'stdout')
Fortran runtime error: Missing initial left parenthesis in format
'(3I15,2I15)'
I don't understand which parenthesis is missing as fmt2 is similar to fmt.
To execute my program I use the gfortran compiler: gfortran -o binary_VariableFormat VariableFormat.F90 and then:./binary_VariableFormat
Second question:
Is there a way to write fmt2 in a more compact way?
This is perhaps more a misunderstanding about character variables and literal constants than about formats.
In
fmt = '(a,f4.2)'
we have the character variable's name (fmt) on the left-hand side of the assignment and the value to assign on the right. '(a,f4.2)' is a literal character constant and its value is given to fmt.
The two, matching, ''s act as delimiters for the value of the constant: they are not themselves part of the value.1 The value of the literal constant is (a,f4.2). Go on, print the value of fmt to check. (One actually has to do something quite different to get the value of a character variable with delimiters.)
A character value in a format must be something like (...): the first non-blank value must be a (. Because you've set the value to be like '(...)' the compiler is (quite rightly) complaining.
Where we have something like
write(str2,'(I10)') nvari
we are using this literal constant which has value (I10) not '(I10)'. If we instead had
write(str2,"'(I10)'") nvari
we'd see exactly the same complaint (or perhaps a complaint when compiling instead of running).
Finally, note that when using a FORMAT statement (many here really don't like such things), we'd not have these delimiters at all:
format (I10) ! Correct
format '(I10)' ! Badly wrong
1 Similarly, if we have literal constants used like
complex :: a = (1., 2.)
integer :: i = INT(B'001')
the delimiters for the complex and BOZ literal constants are not part of the value. Equally in an array constructor like [0,1,2,3] the square brackets are a delimiter part of the syntax rather than the value.

problems with getting substrings from strings in FORTRAN [duplicate]

This question already has answers here:
Extract substring of Fortran string array
(3 answers)
Extract a single character from a Fortran string
(1 answer)
Access character at specific index in a string in Fortran
(1 answer)
Closed 3 years ago.
I would like to evaluate if the 3rd letter of variable myline is 'C' or not.
I try this:
program main
implicit none
type line
integer :: count = 5
character(len=48) :: list = 'ABCDE'
end type
type(line) :: myline
character(len=1) :: letter = 'C'
write(*,*) myline%count, myline%list
if(myline%list(3) == letter) then
write(*,*) 'TRUE'
else
write(*,*) 'FALSE'
end if
end program
But I get:
$ /usr/local/bin/gfortran8 -mcmodel=medium -fcheck=all -Wl,-rpath=/usr/local/lib/gcc8 -o test test.f90
test.f90:15:15:
if(myline%list(3) == letter) then
1
Error: Syntax error in IF-expression at (1)
test.f90:17:5:
else
1
Error: Unexpected ELSE statement at (1)
test.f90:19:4:
end if
1
Error: Expecting END PROGRAM statement at (1)
I am using gfortran (gcc8) and the Fortran 90 standard.
In Fortran, a character substring reference always needs a start and end position. So what you want here is myline%list(3:3).
You can omit the end position (retaining the colon), for example (3:), and that means the rest of the string. Similarly you can omit the start position and it means from the first character (:3).
As a suggestion, letter would be better declared with the parameter attribute as it is a constant, but what you have would work.

How to split string to characters in Fortran?

I am working on ciphering program, where ull put text and it will convert it into coded message. I stick and point where I dont know how to split len into sepparate characters. Is it even possible?
Yes, you can split a string into a character array. If chararray = "abcdefg" then you can have an array like ca(1) = chararray(1:1), ca(2) = chararray(2:2) ...
ca(7:7) = chararray(7)
This can also be done in a do-loop using the iachar and achar functions.
program test
character*7 :: stringin
integer :: single_character
stringin = 'message'
do i = 1, len(stringin), 1
single_character = iachar(stringin(i:i))
write(*,*) achar(single_character)
end do
end program
This creates an integer representation of the letters with iachar and converts them back with achar. This may be helpful if using a mathematical expression to do the encoding.
More information can be found here

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

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