Skip to content

Implement starts_with and ends_with functions #384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions doc/specs/stdlib_strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,87 @@ program demo
print'(a)', chomp("hello", substring="lo") ! "hel"
end program demo
```


<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### `starts_with`

#### Description

Check if a *string* starts with a given *substring*.

#### Syntax

`string = [[stdlib_strings(module):starts_with(interface)]] (string, substring)`

#### Status

Experimental

#### Class

Pure function.

#### Argument

- `string`: Character scalar or [[stdlib_string_type(module):string_type(type)]].
This argument is intent(in).
- `substring`: Character scalar or [[stdlib_string_type(module):string_type(type)]].
This argument is intent(in).

#### Result value

The result is of scalar logical type.

#### Example

```fortran
program demo
use stdlib_strings, only : starts_with
implicit none
print'(a)', starts_with("pattern", "pat") ! T
print'(a)', starts_with("pattern", "ern") ! F
end program demo
```


<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
### `ends_with`

#### Description

Check if a *string* ends with a given *substring*.

#### Syntax

`string = [[stdlib_strings(module):ends_with(interface)]] (string, substring)`

#### Status

Experimental

#### Class

Pure function.

#### Argument

- `string`: Character scalar or [[stdlib_string_type(module):string_type(type)]].
This argument is intent(in).
- `substring`: Character scalar or [[stdlib_string_type(module):string_type(type)]].
This argument is intent(in).

#### Result value

The result is of scalar logical type.

#### Example

```fortran
program demo
use stdlib_strings, only : ends_with
implicit none
print'(a)', ends_with("pattern", "ern") ! T
print'(a)', ends_with("pattern", "pat") ! F
end program demo
```
118 changes: 118 additions & 0 deletions src/stdlib_strings.f90
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module stdlib_strings
private

public :: strip, chomp
public :: starts_with, ends_with


!> Remove leading and trailing whitespace characters.
Expand All @@ -36,6 +37,28 @@ module stdlib_strings
end interface chomp


!> Check whether a string starts with substring or not
!>
!> Version: experimental
interface starts_with
module procedure :: starts_with_string_string
module procedure :: starts_with_string_char
module procedure :: starts_with_char_string
module procedure :: starts_with_char_char
end interface starts_with


!> Check whether a string ends with substring or not
!>
!> Version: experimental
interface ends_with
module procedure :: ends_with_string_string
module procedure :: ends_with_string_char
module procedure :: ends_with_char_string
module procedure :: ends_with_char_char
end interface ends_with


contains


Expand Down Expand Up @@ -173,4 +196,99 @@ pure function set_to_string(set) result(string)
end function set_to_string


!> Check whether a string starts with substring or not
pure function starts_with_char_char(string, substring) result(match)
character(len=*), intent(in) :: string
character(len=*), intent(in) :: substring
logical :: match
integer :: nsub

nsub = len(substring)
if (len(string) < nsub) then
match = .false.
return
end if
match = string(1:nsub) == substring

end function starts_with_char_char

!> Check whether a string starts with substring or not
elemental function starts_with_string_char(string, substring) result(match)
type(string_type), intent(in) :: string
character(len=*), intent(in) :: substring
logical :: match

match = starts_with(char(string), substring)

end function starts_with_string_char

!> Check whether a string starts with substring or not
elemental function starts_with_char_string(string, substring) result(match)
character(len=*), intent(in) :: string
type(string_type), intent(in) :: substring
logical :: match

match = starts_with(string, char(substring))

end function starts_with_char_string

!> Check whether a string starts with substring or not
elemental function starts_with_string_string(string, substring) result(match)
type(string_type), intent(in) :: string
type(string_type), intent(in) :: substring
logical :: match

match = starts_with(char(string), char(substring))

end function starts_with_string_string


!> Check whether a string ends with substring or not
pure function ends_with_char_char(string, substring) result(match)
character(len=*), intent(in) :: string
character(len=*), intent(in) :: substring
logical :: match
integer :: last, nsub

last = len(string)
nsub = len(substring)
if (last < nsub) then
match = .false.
return
end if
match = string(last-nsub+1:last) == substring

end function ends_with_char_char

!> Check whether a string ends with substring or not
elemental function ends_with_string_char(string, substring) result(match)
type(string_type), intent(in) :: string
character(len=*), intent(in) :: substring
logical :: match

match = ends_with(char(string), substring)

end function ends_with_string_char

!> Check whether a string ends with substring or not
elemental function ends_with_char_string(string, substring) result(match)
character(len=*), intent(in) :: string
type(string_type), intent(in) :: substring
logical :: match

match = ends_with(string, char(substring))

end function ends_with_char_string

!> Check whether a string ends with substring or not
elemental function ends_with_string_string(string, substring) result(match)
type(string_type), intent(in) :: string
type(string_type), intent(in) :: substring
logical :: match

match = ends_with(char(string), char(substring))

end function ends_with_string_string


end module stdlib_strings
1 change: 1 addition & 0 deletions src/tests/string/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ADDTEST(string_assignment)
ADDTEST(string_operator)
ADDTEST(string_intrinsic)
ADDTEST(string_match)
ADDTEST(string_derivedtype_io)
ADDTEST(string_functions)
ADDTEST(string_strip_chomp)
1 change: 1 addition & 0 deletions src/tests/string/Makefile.manual
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ PROGS_SRC = test_string_assignment.f90 \
test_string_derivedtype_io.f90 \
test_string_functions.f90 \
test_string_intrinsic.f90 \
test_string_match.f90 \
test_string_operator.f90 \
test_string_strip_chomp.f90

Expand Down
72 changes: 72 additions & 0 deletions src/tests/string/test_string_match.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
! SPDX-Identifier: MIT
module test_match
use stdlib_ascii, only : reverse
use stdlib_error, only : check
use stdlib_strings, only : starts_with, ends_with
use stdlib_string_type, only : string_type
implicit none

contains

subroutine check_starts_with(string, substring)
character(len=*), intent(in) :: string
character(len=*), intent(in) :: substring
logical :: match
character(len=:), allocatable :: message

match = index(string, substring) == 1
if (match) then
message = "Failed to recognize that '"//string//"' starts with '"//substring//"'"
else
message = "Incorrectly found that '"//string//"' starts with '"//substring//"'"
end if

call check(starts_with(string, substring) .eqv. match, message)
call check(starts_with(string_type(string), substring) .eqv. match, message)
call check(starts_with(string, string_type(substring)) .eqv. match, message)
call check(starts_with(string_type(string), string_type(substring)) .eqv. match, message)
end subroutine check_starts_with

subroutine test_starts_with
call check_starts_with("pattern", "pat")
call check_starts_with("pat", "pattern")
call check_starts_with("pattern", "ern")
call check_starts_with("ern", "pattern")
end subroutine test_starts_with

subroutine check_ends_with(string, substring)
character(len=*), intent(in) :: string
character(len=*), intent(in) :: substring
logical :: match
character(len=:), allocatable :: message

match = index(reverse(string), reverse(substring)) == 1
if (match) then
message = "Failed to recognize that '"//string//"' ends with '"//substring//"'"
else
message = "Incorrectly found that '"//string//"' ends with '"//substring//"'"
end if

call check(ends_with(string, substring) .eqv. match, message)
call check(ends_with(string_type(string), substring) .eqv. match, message)
call check(ends_with(string, string_type(substring)) .eqv. match, message)
call check(ends_with(string_type(string), string_type(substring)) .eqv. match, message)
end subroutine check_ends_with

subroutine test_ends_with
call check_ends_with("pattern", "pat")
call check_ends_with("pat", "pattern")
call check_ends_with("pattern", "ern")
call check_ends_with("ern", "pattern")
end subroutine test_ends_with

end module test_match

program tester
use test_match
implicit none

call test_starts_with
call test_ends_with

end program tester