Skip to content

Commit 8a86721

Browse files
committed
bug fix in uppercase/lowercase functions
added some more string functions and unit tests.
1 parent 213ec4d commit 8a86721

File tree

2 files changed

+139
-9
lines changed

2 files changed

+139
-9
lines changed

src/string_module.f90

Lines changed: 105 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ module string_module
1313
character(len=*),parameter :: upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' !! uppercase characters
1414

1515
public :: lowercase,uppercase
16+
public :: replace_char
17+
public :: reverse
18+
public :: lchop, rchop
1619

1720
contains
1821
!*****************************************************************************************
@@ -21,16 +24,14 @@ module string_module
2124
!>
2225
! Convert the string to uppercase.
2326

24-
subroutine uppercase(str)
25-
26-
implicit none
27+
pure subroutine uppercase(str)
2728

2829
character(len=*),intent(inout) :: str
2930

3031
integer :: i,idx
3132

3233
do i=1,len_trim(str)
33-
idx = index(str(i:i),lower)
34+
idx = index(lower,str(i:i))
3435
if (idx>0) str(i:i) = upper(idx:idx)
3536
end do
3637

@@ -41,21 +42,116 @@ end subroutine uppercase
4142
!>
4243
! Convert the string to lowercase.
4344

44-
subroutine lowercase(str)
45-
46-
implicit none
45+
pure subroutine lowercase(str)
4746

4847
character(len=*),intent(inout) :: str
4948

5049
integer :: i,idx
5150

5251
do i=1,len_trim(str)
53-
idx = index(str(i:i),upper)
52+
idx = index(upper,str(i:i))
5453
if (idx>0) str(i:i) = lower(idx:idx)
5554
end do
5655

5756
end subroutine lowercase
5857
!*****************************************************************************************
5958

60-
end module string_module
6159
!*****************************************************************************************
60+
!>
61+
! Replace all occurrences of a single character `s1` in `str` with `s2`.
62+
63+
pure function replace_char(str, s1, s2) result(newstr)
64+
65+
character(len=*),intent(in) :: str !! original string
66+
character(len=1),intent(in) :: s1 !! find all occurrences of this character
67+
character(len=1),intent(in) :: s2 !! replace with this character
68+
character(len=:),allocatable :: newstr !! new string
69+
70+
integer :: i !! counter
71+
72+
newstr = str
73+
do i = 1, len(newstr)
74+
if (newstr(i:i) == s1) newstr(i:i) = s2
75+
end do
76+
77+
end function replace_char
78+
!*****************************************************************************************
79+
80+
!*****************************************************************************************
81+
!>
82+
! Chop leading `chars` string from `str`.
83+
! Note that trailing spaces are not ignored in either string.
84+
85+
pure function lchop(str, chars) result(newstr)
86+
87+
character(len=*),intent(in) :: str !! original string
88+
character(len=*),intent(in) :: chars !! characters to strip
89+
character(len=:),allocatable :: newstr !! new string
90+
91+
! this logic here is to account for trailing spaces, which we preserve
92+
if (len(chars)>len(str)) then
93+
newstr = str ! not possible to chop
94+
else
95+
if (str==chars) then
96+
if (len(str)>len(chars)) then
97+
newstr = str(len(chars)+1:) ! only trailing spaces remain
98+
else
99+
newstr = '' ! string is now empty
100+
end if
101+
else
102+
if (index(str,chars) == 1) then
103+
newstr = str(len(chars)+1:) ! chop leading chars, keep rest of string
104+
else
105+
newstr = str ! original string, noting to chop
106+
end if
107+
end if
108+
end if
109+
110+
end function lchop
111+
!*****************************************************************************************
112+
113+
!*****************************************************************************************
114+
!>
115+
! Chop trailing `chars` string from `str`.
116+
! Note that trailing spaces are not ignored in either string.
117+
118+
pure function rchop(str, chars) result(newstr)
119+
120+
character(len=*),intent(in) :: str !! original string
121+
character(len=*),intent(in) :: chars !! characters to strip
122+
character(len=:),allocatable :: newstr !! new string
123+
124+
newstr = reverse(lchop(reverse(str), reverse(chars)))
125+
126+
end function rchop
127+
!*****************************************************************************************
128+
129+
!*****************************************************************************************
130+
!>
131+
! Reverse a string.
132+
133+
pure function reverse(str) result(newstr)
134+
135+
character(len=*),intent(in) :: str !! original string
136+
character(len=:),allocatable :: newstr !! new string
137+
integer :: i, j !! counter
138+
integer :: n !! length of `str`
139+
140+
n = len(str)
141+
allocate(character(len=n) :: newstr)
142+
if (n==0) then
143+
newstr = ''
144+
else
145+
j = 0
146+
do i = n, 1, -1
147+
j = j + 1
148+
newstr(j:j) = str(i:i)
149+
end do
150+
end if
151+
152+
end function reverse
153+
!*****************************************************************************************
154+
155+
!*****************************************************************************************
156+
end module string_module
157+
!*****************************************************************************************

test/string_test.f90

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
!*****************************************************************************************
2+
!>
3+
! Test program for the string routines.
4+
5+
program string_test
6+
7+
use string_module
8+
9+
implicit none
10+
11+
character(len=:),allocatable :: s1
12+
13+
s1 = 'Hello, World!'
14+
call uppercase(s1)
15+
if (s1 /= 'HELLO, WORLD!') error stop 'uppercase test failed: '//s1
16+
17+
s1 = 'Hello, World!'
18+
call lowercase(s1)
19+
if (s1 /= 'hello, world!') error stop 'lowercase test failed: '//s1
20+
21+
s1 = replace_char('banana', 'a', 'o')
22+
if (s1 /= 'bonono') error stop 'replace char test failed: '//s1
23+
24+
s1 = reverse('stressed')
25+
if (s1 /= 'desserts') error stop 'reverse test failed: '//s1
26+
27+
s1 = lchop('abc efg', 'abc')
28+
if (s1 /= ' efg' .and. len(s1)/=4) error stop 'lchop test failed: '//s1
29+
30+
s1 = rchop('abc efg', 'efg')
31+
if (s1 /= 'abc' .and. s1(4:4) /= ' ') error stop 'rchop test failed: '//s1
32+
33+
end program string_test
34+
!*****************************************************************************************

0 commit comments

Comments
 (0)