diff --git a/doc/specs/stdlib_strings.md b/doc/specs/stdlib_strings.md index 087589bbc..9df9d3cfd 100644 --- a/doc/specs/stdlib_strings.md +++ b/doc/specs/stdlib_strings.md @@ -384,6 +384,114 @@ end program demo_replace_all ``` + +### `padl` + +#### Description + +Returns a string of length `output_length` left padded with `pad_with` character if it is provided, otherwise with `" "` (1 whitespace). +If `output_length` is less than or equal to the length of `string`, padding is not performed. + +#### Syntax + +`string = [[stdlib_strings(module):padl(interface)]] (string, output_length [, pad_with])` + +#### Status + +Experimental + +#### Class + +Pure function + +#### Argument + +- `string`: Character scalar or [[stdlib_string_type(module):string_type(type)]]. + This argument is intent(in). +- `output_length`: integer. + This argument is intent(in). +- `pad_with`: Character scalar of length 1. + This argument is intent(in) and optional. + +#### Result value + +The result is of the same type as `string`. + +#### Example + +```fortran +program demo_padl + use stdlib_string_type, only: string_type, assignment(=) + use stdlib_strings, only : padl + implicit none + string_type :: string + + string = "left pad this string" + ! string <-- "left pad this string" + + print *, padl(string, 25, "$") ! "$$$$$left pad this string" + + string = padl(string, 25) + ! string <-- " left pad this string" + +end program demo_padl +``` + + + +### `padr` + +#### Description + +Returns a string of length `output_length` right padded with `pad_with` character if it is provided, otherwise with `" "` (1 whitespace). +If `output_length` is less than or equal to the length of `string`, padding is not performed. + +#### Syntax + +`string = [[stdlib_strings(module):padr(interface)]] (string, output_length [, pad_with])` + +#### Status + +Experimental + +#### Class + +Pure function + +#### Argument + +- `string`: Character scalar or [[stdlib_string_type(module):string_type(type)]]. + This argument is intent(in). +- `output_length`: integer. + This argument is intent(in). +- `pad_with`: Character scalar of length 1. + This argument is intent(in) and optional. + +#### Result value + +The result is of the same type as `string`. + +#### Example + +```fortran +program demo_padr + use stdlib_string_type, only: string_type, assignment(=) + use stdlib_strings, only : padr + implicit none + string_type :: string + + string = "right pad this string" + ! string <-- "right pad this string" + + print *, padr(string, 25, "$") ! "right pad this string$$$$" + + string = padr(string, 25) + ! string <-- "right pad this string " + +end program demo_padr +``` + + ### `count` diff --git a/src/stdlib_strings.f90 b/src/stdlib_strings.f90 index 2554dbfb1..095ac93b9 100644 --- a/src/stdlib_strings.f90 +++ b/src/stdlib_strings.f90 @@ -5,14 +5,14 @@ !> The specification of this module is available [here](../page/specs/stdlib_strings.html). module stdlib_strings use stdlib_ascii, only: whitespace - use stdlib_string_type, only: string_type, char, verify + use stdlib_string_type, only: string_type, char, verify, repeat, len use stdlib_optval, only: optval implicit none private public :: strip, chomp public :: starts_with, ends_with - public :: slice, find, replace_all, count + public :: slice, find, replace_all, padl, padr, count !> Remove leading and trailing whitespace characters. @@ -93,6 +93,28 @@ module stdlib_strings module procedure :: replace_all_char_char_char end interface replace_all + !> Version: experimental + !> + !> Left pad the input string + !> [Specifications](../page/specs/stdlib_strings.html#padl) + interface padl + module procedure :: padl_string_default + module procedure :: padl_string_pad_with + module procedure :: padl_char_default + module procedure :: padl_char_pad_with + end interface padl + + !> Version: experimental + !> + !> Right pad the input string + !> [Specifications](../page/specs/stdlib_strings.html#padr) + interface padr + module procedure :: padr_string_default + module procedure :: padr_string_pad_with + module procedure :: padr_char_default + module procedure :: padr_char_pad_with + end interface padr + !> Version: experimental !> !> Returns the number of times substring 'pattern' has appeared in the @@ -659,6 +681,127 @@ pure function replace_all_char_char_char(string, pattern, replacement) result(re end function replace_all_char_char_char + !> Left pad the input string with " " (1 whitespace) + !> + !> Returns a new string + pure function padl_string_default(string, output_length) result(res) + type(string_type), intent(in) :: string + integer, intent(in) :: output_length + type(string_type) :: res + + res = string_type(padl(char(string), output_length, " ")) + + end function padl_string_default + + !> Left pad the input string with the 'pad_with' character + !> + !> Returns a new string + pure function padl_string_pad_with(string, output_length, pad_with) result(res) + type(string_type), intent(in) :: string + integer, intent(in) :: output_length + character(len=1), intent(in) :: pad_with + type(string_type) :: res + + res = string_type(padl(char(string), output_length, pad_with)) + + end function padl_string_pad_with + + !> Left pad the input string with " " (1 whitespace) + !> + !> Returns a new string + pure function padl_char_default(string, output_length) result(res) + character(len=*), intent(in) :: string + integer, intent(in) :: output_length + character(len=max(len(string), output_length)) :: res + + res = padl(string, output_length, " ") + + end function padl_char_default + + !> Left pad the input string with the 'pad_with' character + !> + !> Returns a new string + pure function padl_char_pad_with(string, output_length, pad_with) result(res) + character(len=*), intent(in) :: string + integer, intent(in) :: output_length + character(len=1), intent(in) :: pad_with + character(len=max(len(string), output_length)) :: res + integer :: string_length + + string_length = len(string) + + if (string_length < output_length) then + res = repeat(pad_with, output_length - string_length) + res(output_length - string_length + 1 : output_length) = string + else + res = string + end if + + end function padl_char_pad_with + + !> Right pad the input string with " " (1 whitespace) + !> + !> Returns a new string + pure function padr_string_default(string, output_length) result(res) + type(string_type), intent(in) :: string + integer, intent(in) :: output_length + character(len=max(len(string), output_length)) :: char_output + type(string_type) :: res + + ! We're taking advantage of `char_output` being longer than `string` and + ! initialized with whitespaces. By casting `string` to a `character` + ! type and back to `string_type`, we're effectively right-padding + ! `string` with spaces, so we don't need to pad explicitly. + char_output = char(string) + res = string_type(char_output) + + end function padr_string_default + + !> Right pad the input string with the 'pad_with' character + !> + !> Returns a new string + pure function padr_string_pad_with(string, output_length, pad_with) result(res) + type(string_type), intent(in) :: string + integer, intent(in) :: output_length + character(len=1), intent(in) :: pad_with + type(string_type) :: res + + res = string_type(padr(char(string), output_length, pad_with)) + + end function padr_string_pad_with + + !> Right pad the input string with " " (1 whitespace) + !> + !> Returns a new string + pure function padr_char_default(string, output_length) result(res) + character(len=*), intent(in) :: string + integer, intent(in) :: output_length + character(len=max(len(string), output_length)) :: res + + res = string + + end function padr_char_default + + !> Right pad the input string with the 'pad_with' character + !> + !> Returns a new string + pure function padr_char_pad_with(string, output_length, pad_with) result(res) + character(len=*), intent(in) :: string + integer, intent(in) :: output_length + character(len=1), intent(in) :: pad_with + character(len=max(len(string), output_length)) :: res + integer :: string_length + + string_length = len(string) + + res = string + if (string_length < output_length) then + res(string_length + 1 : output_length) = & + repeat(pad_with, output_length - string_length) + end if + + end function padr_char_pad_with + !> Returns the number of times substring 'pattern' has appeared in the !> input string 'string' !> Returns an integer diff --git a/src/tests/string/test_string_functions.f90 b/src/tests/string/test_string_functions.f90 index ca44d956d..4b5f78425 100644 --- a/src/tests/string/test_string_functions.f90 +++ b/src/tests/string/test_string_functions.f90 @@ -4,7 +4,7 @@ module test_string_functions use stdlib_error, only : check use stdlib_string_type, only : string_type, assignment(=), operator(==), & to_lower, to_upper, to_title, to_sentence, reverse - use stdlib_strings, only: slice, find, replace_all, count + use stdlib_strings, only: slice, find, replace_all, padl, padr, count use stdlib_optval, only: optval use stdlib_ascii, only : to_string implicit none @@ -378,6 +378,80 @@ subroutine test_replace_all end subroutine test_replace_all + subroutine test_padl + type(string_type) :: test_string + character(len=:), allocatable :: test_char + + test_string = "left pad this string" + test_char = " left pad this string " + + ! output_length > len(string) + call check(padl(test_string, 25, "#") == "#####left pad this string", & + & 'padl: output_length > len(string), test_case 1') + call check(padl(test_string, 22, "$") == "$$left pad this string", & + & 'padl: output_length > len(string), test_case 2') + call check(padl(test_string, 23) == " left pad this string", & + & 'padl: output_length > len(string), test_case 3') + call check(padl(test_char, 26) == " left pad this string ", & + & 'padl: output_length > len(string), test_case 4') + call check(padl(test_char, 26, "&") == "&& left pad this string ", & + & 'padl: output_length > len(string), test_case 5') + call check(padl("", 10, "!") == "!!!!!!!!!!", & + & 'padl: output_length > len(string), test_case 6') + + ! output_length <= len(string) + call check(padl(test_string, 18, "#") == "left pad this string", & + & 'padl: output_length <= len(string), test_case 1') + call check(padl(test_string, -4, "@") == "left pad this string", & + & 'padl: output_length <= len(string), test_case 2') + call check(padl(test_char, 20, "0") == " left pad this string ", & + & 'padl: output_length <= len(string), test_case 3') + call check(padl(test_char, 17) == " left pad this string ", & + & 'padl: output_length <= len(string), test_case 4') + call check(padl("", 0, "!") == "", & + & 'padl: output_length <= len(string), test_case 5') + call check(padl("", -12, "!") == "", & + & 'padl: output_length <= len(string), test_case 6') + + end subroutine test_padl + + subroutine test_padr + type(string_type) :: test_string + character(len=:), allocatable :: test_char + + test_string = "right pad this string" + test_char = " right pad this string " + + ! output_length > len(string) + call check(padr(test_string, 25, "#") == "right pad this string####", & + & 'padr: output_length > len(string), test_case 1') + call check(padr(test_string, 22, "$") == "right pad this string$", & + & 'padr: output_length > len(string), test_case 2') + call check(padr(test_string, 24) == "right pad this string ", & + & 'padr: output_length > len(string), test_case 3') + call check(padr(test_char, 27) == " right pad this string ", & + & 'padr: output_length > len(string), test_case 4') + call check(padr(test_char, 27, "&") == " right pad this string &&", & + & 'padr: output_length > len(string), test_case 5') + call check(padr("", 10, "!") == "!!!!!!!!!!", & + & 'padr: output_length > len(string), test_case 6') + + ! output_length <= len(string) + call check(padr(test_string, 18, "#") == "right pad this string", & + & 'padr: output_length <= len(string), test_case 1') + call check(padr(test_string, -4, "@") == "right pad this string", & + & 'padr: output_length <= len(string), test_case 2') + call check(padr(test_char, 20, "0") == " right pad this string ", & + & 'padr: output_length <= len(string), test_case 3') + call check(padr(test_char, 17) == " right pad this string ", & + & 'padr: output_length <= len(string), test_case 4') + call check(padr("", 0, "!") == "", & + & 'padr: output_length <= len(string), test_case 5') + call check(padr("", -12, "!") == "", & + & 'padr: output_length <= len(string), test_case 6') + + end subroutine test_padr + subroutine test_count type(string_type) :: test_string_1, test_string_2, test_pattern_1, test_pattern_2 test_string_1 = "DNA sequence: AGAGAGAGTCCTGTCGAGA" @@ -437,6 +511,8 @@ program tester call test_slice_gen call test_find call test_replace_all + call test_padl + call test_padr call test_count end program tester