Извлечь подстроку из строкового массива Фортрана

Как извлечь подстроку из массива строк Фортрана? Например

program testcharindex
    implicit none
    character(len=10), dimension(5) :: s
    character(len=10), allocatable :: comp(:)
    integer, allocatable  :: i(:), n(:)
    s = (/ '1_E ', '2_S ', '3_E ', '14_E', '25_S' /)
    i = index(s,'_')
    print *,' i = ', i
    n = s(1:i-1) ! or n = s(:i-1)
    comp = s(i+1:)
    print *,' n = ', n
    print *,' comp = ', comp
end program

Компиляция с gfortran приводит к ошибке:

testcharindex.f90: 11: 10:

n = s (1: i-1) 1 Ошибка: индекс массива в (1) должен быть скалярным

Есть ли способ избежать цикла do здесь? Если можно извлечь индекс массива строк, я ожидаю, что он сможет извлечь динамически определенную подстроку массива строк (без зацикливания на элементах массива). Я слишком оптимистичен?

3 ответа

В случае, когда следует избегать цикла и нет других (простых) методов, может быть полезно определить элементарную функцию подстроки и применить ее к массиву строк. Например,

module str_mod
    implicit none
contains
    elemental function substr( s, a, b ) result( res )
        character(*), intent(in) :: s
        integer,      intent(in) :: a, b
        character(len(s)) :: res

        res = s( a : b )
    endfunction
endmodule

program main
    use str_mod
    implicit none
    character(10) :: s( 5 )
    integer, allocatable :: ind(:)
    character(len(s)), allocatable :: comp(:)

    s = [ '1_E ', '2_S ', '3_E ', '14_E', '25_S' ]
    ! s = [ character(len(s)) :: '1_E', '2_S', '3_E', '14_E', '25_S' ]

    print *, "test(scalar) : ", substr( s(1), 1, 2 )
    print *, "test(array ) : ", substr( s,    1, 2 )

    ind = index( s, '_' )
    comp = substr( s, 1, ind-1 )

    print *
    print *, "string (all)    : ", s
    print *, "string before _ : ", comp
    print *, "string after _  : ", substr( s, ind+1, len(s) )
endprogram

который дает (с gfortran-7.3)

 test(scalar) : 1_        
 test(array ) : 1_        2_        3_        14        25        

 string (all)    : 1_E       2_S       3_E       14_E      25_S      
 string before _ : 1         2         3         14        25        
 string after _  : E         S         E         E         S     

У вас есть пара проблем здесь. Один из которых легко решается (и был в других вопросах: вы можете найти их для более подробной информации).

Линия1

n = s(1:i-1)

о чем жалуется компилятор - это попытка сослаться на часть массива s, а не массив подстрок элементов массива s, Для доступа к подстрокам массива вам понадобится

n = s(:)(1:i-1)

Однако это связано с вашей второй проблемой. Поскольку компилятор жалуется на доступ к разделу массива, i должен быть скаляр. Это также верно для случая доступа к подстрокам массива. Вышеуказанная строка все равно не будет работать.

По сути, если вы хотите получить доступ к подстрокам массива, каждая подстрока должна иметь точно такую ​​же структуру. То есть в s(:)(i:j) и то и другое i а также j должны быть скалярными целочисленными выражениями. Это вызвано желанием, чтобы каждый элемент возвращаемого массива был одинаковой длины.

Тогда вам нужно будет использовать цикл.


1 Как однажды прокомментировал High Performance Mark, есть проблема с самим назначением. Я рассмотрел просто выражение с правой стороны. Даже исправленное для правильной подстроки массива, выражение все еще является массивом символов, который не может быть назначен на целочисленный скаляр n по желанию.

Если вы хотите получить буквальный ответ о выборе подстрок, прочитайте, как указано выше. Если вы просто заботитесь о "преобразовании части массива символов в массив целых чисел", тогда другой ответ хорошо охватывает все.

@francescalus уже объяснил ошибку, вот мой вклад в вопрос OP, кажется, действительно решает, то есть, как читать целые числа из массива строк, таких как

s = (/ '1_E ', '2_S ', '3_E ', '14_E', '25_S' /)

OP хочет сделать это без циклов, а @roygvib указывает нам на использование элементарной функции. Вот моя версия такой функции для чтения целого числа из строки. Это игнорирует любые ведущие пробелы, поэтому должны справляться с такими строками, как 12_e, Затем он прекращает сканирование с первого нецифрового символа (поэтому читает 12 из строки, такой как 12_3).

ELEMENTAL INTEGER FUNCTION read_int(str)
  CHARACTER(*), INTENT(in) :: str
  CHARACTER(:), ALLOCATABLE :: instr

  instr = adjustl(str)
  instr = instr(1:VERIFY(instr,'0123456789')-1)
  ! if the string doesn't have a leading digit instr will be empty, return a guard value
  IF(instr=='') instr = '-999'
  READ(instr,*) read_int
END FUNCTION read_int

Я верю, что это достаточно ясно. ОП мог тогда написать

n = read_int(s)
Другие вопросы по тегам