String split in gfortran

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.

$
4 Likes

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

According to the changelog on the GCC site,

Version 16 (which will probably officially released the next spring) will be the first version to support it.

Thanks! I must have been looking at the wrong version of the source code.

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