Is the new 2023 standard string split subroutine (16.9.196) not implemented in gfortran? I scanned the gcc source code and found that string_split was defined in gcc/libgfortran/intrinsics/string_intrinsics_inc.c and gfortran has the it in the documentation.
On gcc/gfortran 15.2.1, I can’t compile the program (undefined reference). I can compile on the intel ifx compiler. Here’s the sample I was trying to test it out with.
program main
implicit none
integer :: i, j, p, n
character(*), parameter :: input = "a,b,c,d,"
n = 0
p = 0
do while(p <= len(input))
i = p + 1
call split(input, ",", p)
j = p - 1
n = n + 1
print "(I0,4x,A)", n, input(i:j)
end do
end program main
It is in the latest builds. My version isn’t quite up to date, but your example seems to work fine:
$ gfortran string_split.f90
$ ./a.out
1 a
2 b
3 c
4 d
5
$ gfortran --version
GNU Fortran (GCC) 16.0.0 20251209 (experimental)
Copyright (C) 2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$
A split procedure from 2020 by @milancurcic if needed that can be contained or placed in a module
split.f90
!!##AUTHOR
!! Milan Curcic, "milancurcic@hey.com"
!!
!!##LICENSE
!! Public Domain
!!
!!##VERSION
!! version 0.1.0, copyright 2020, Milan Curcic
pure subroutine split(string, set, pos, back)
! If back is absent, computes the leftmost token delimiter in string whose
! position is > pos. If back is present and true, computes the rightmost
! token delimiter in string whose position is < pos. The result is stored
! in pos.
character(*), intent(in) :: string
character(*), intent(in) :: set
integer, intent(in out) :: pos
logical, intent(in), optional :: back
logical :: backward
character :: set_array(len(set))
integer :: n, result_pos
!TODO use optval when implemented in stdlib
!backward = optval(back, .false.)
backward = .false.
if (present(back)) backward = back
do concurrent(n=1:len(set))
set_array(n) = set(n:n)
enddo
if (backward) then
result_pos = 0
do n = pos - 1, 1, -1
if (any(string(n:n) == set_array)) then
result_pos = n
exit
endif
enddo
else
result_pos = len(string) + 1
do n = pos + 1, len(string)
if (any(string(n:n) == set_array)) then
result_pos = n
exit
endif
enddo
endif
pos = result_pos
end subroutine split
Thanks I was using a crude strtok-like function that I wrote with another function to aggregate the records to an array.
subroutine strtok(input_string, substring, delimiter)
character(:), allocatable, intent(in out) :: input_string
character(:), allocatable, intent(out) :: substring
character(*), intent(in) :: delimiter
integer :: i
i = index(input_string, delimiter)
if (i < 1) then
substring = input_string
else if (i == 1) then
substring = ""
else
substring = input_string(1:i - 1)
end if
if (substring == input_string .or. (substring == "" .and. len(input_string) == 0)) then
deallocate(input_string)
else
input_string = input_string(i + len(delimiter):)
end if
end subroutine strtok
function tokenize(input_string, delimiter) result(tokens)
character(*), intent(in) :: input_string, delimiter
character(:), allocatable :: tmp_string, tokens(:), substring
integer :: elements, max_len, i
tmp_string = input_string
elements = 0
max_len = 0
do while (allocated(tmp_string))
call strtok(tmp_string, substring, delimiter)
max_len = max(max_len, len(substring))
elements = elements + 1
end do
allocate(character(max_len) :: tokens(elements))
tmp_string = input_string
if (elements > 0) then
do i = 1, elements
call strtok(tmp_string, substring, delimiter)
tokens(i) = substring
end do
else
tokens(1) = input_string
end if
end function tokenize