Оператор записи не может создавать новые строки в пользовательских форматированных процедурах ввода-вывода для производного типа.

Я хочу реализовать определяемые пользователем процедуры ввода-вывода для производных типов в моем коде Fortran. Однако операторы write в этих процедурах не могут создавать новые строки между двумя последовательными операторами write. Производный тип и процедуры определены ниже.

Модуль:

module station_module
    implicit none

    character(8), parameter :: FmtFloat = '(5E15.7)'

    type :: station
        integer, private :: ns = 0
        real, public, allocatable :: xloc(:), yloc(:), zloc(:)
    contains
        procedure, public :: import_station
        procedure, public :: export_station
        procedure, private :: read_station
        generic, public :: read (formatted) => read_station
        procedure, private :: write_station
        generic, public :: write (formatted) => write_station
        final :: destruct_station
    end type station

    interface station
        module procedure new_station
    end interface station

contains

    function new_station(n) result(t)
        implicit none
        integer, intent(in) :: n
        type(station) :: t

        if (n > 0) then
            allocate (t%zloc(n))
            allocate (t%yloc(n))
            allocate (t%xloc(n))
            t%ns = n
        end if
    end function new_station

    subroutine read_station(dtv, unit, iotype, vlist, iostat, iomsg)
        implicit none
        class(station), intent(inout) :: dtv
        integer, intent(in) :: unit
        character(*), intent(in) :: iotype
        integer, intent(in) :: vlist(:)
        integer, intent(out) :: iostat
        character(*), intent(inout) :: iomsg

        call dtv%import_station(unit)

        iostat = 0
    end subroutine read_station

    subroutine import_station(this, unit)
        implicit none
        class(station), intent(inout) :: this
        integer, intent(in) :: unit
        character(256) :: header, footer
        integer ns

        read (unit, '(A)') header !> Header
        read (unit, *) ns
        if (ns > 0) then
            if (allocated(this%zloc)) then
                deallocate (this%zloc)
            end if
            allocate (this%zloc(ns))
            read (unit, *) this%zloc

            if (allocated(this%yloc)) then
                deallocate (this%yloc)
            end if
            allocate (this%yloc(ns))
            read (unit, *) this%yloc

            if (allocated(this%xloc)) then
                deallocate (this%xloc)
            end if
            allocate (this%xloc(ns))
            read (unit, *) this%xloc

            this%ns = ns
        end if
        read (unit, '(A)') footer !> Footer
    end subroutine import_station

    subroutine export_station(this, unit)
        implicit none
        class(station), intent(in) :: this
        integer, intent(in) :: unit

        write (unit, '(A)') ">STATION INFO"
        write (unit, '(I6)') this%ns
        write (unit, *) "Z:"
        write (unit, FmtFloat) this%zloc
        write (unit, *) "Y:"
        write (unit, FmtFloat) this%yloc
        write (unit, *) "X:"
        write (unit, FmtFloat) this%xloc
        write (unit, '(A)') ">END STATION"
    end subroutine export_station

    subroutine write_station(dtv, unit, iotype, vlist, iostat, iomsg)
        implicit none
        class(station), intent(in) :: dtv
        integer, intent(in) :: unit
        character(*), intent(in) :: iotype
        integer, intent(in) :: vlist(:)
        integer, intent(out) :: iostat
        character(*), intent(inout) :: iomsg

        call dtv%export_station(unit)

        iostat = 0
    end subroutine write_station

    subroutine destruct_station(this)
        implicit none
        type(station), intent(inout) :: this

        if (allocated(this%xloc)) then
            deallocate (this%xloc)
        end if
        if (allocated(this%yloc)) then
            deallocate (this%yloc)
        end if
        if (allocated(this%zloc)) then
            deallocate (this%zloc)
        end if
        this%ns = 0
    end subroutine destruct_station

end module station_module

Мы видим, что определяемый пользователем оператор форматированной записи просто вызывает обычную подпрограмму с именем export_station, от которой я ожидаю того же результата в обоих случаях.

Вот моя тестовая программа:

program Test
    use station_module
    implicit none
    type(station) :: pt, pt1, pt2

    pt = station(4)

    write(*, *) pt

    call pt%export_station(6)

end program Test

Результат:

 >STATION INFO     4Z:  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00
 Y:  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00X:  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00>END STATION
>STATION INFO
     4
 Z:
  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00
 Y:
  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00
 X:
  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00
>END STATION

Обычная подпрограмма export_station производит то, что я ожидал. Новые строки создаются между двумя операторами write, а оператор write производного типа - нет.


person Rubin    schedule 26.12.2017    source источник


Ответы (2)


Об этом также спрашивали на форуме Intel. Я ответил: «Пользовательский ввод-вывод производного типа не является продвигающимся (и вы не можете это изменить). Если вам нужны символы новой строки, вы должны написать их явно (например, используя формат /)».

person Steve Lionel    schedule 26.12.2017
comment
Чтобы сделать это более явным: что-то вроде WRITE(unit,'(a/)') делает свое дело. - person Thomas Kühn; 23.05.2019

Здесь есть два класса операторов вывода: родительский и дочерний. Оператор родительского вывода в первом случае - это write (*,*) pt.

Когда это первый родитель, тогда вызов от export_station до write_station приводит к операторам записи, которые являются дочерними операторами вывода. Когда export_station вызывается пользователем напрямую, эти операторы записи сами являются родительскими операторами вывода.

Одно существенное различие между дочерним оператором передачи данных и родительским оператором передачи данных заключается в том, что родительский оператор позиционирует файл до и после передачи данных. То есть, когда write (unit,*) "Z:" завершается, файл располагается после только что записанной записи, только если оператор передачи является родительским.

Таким образом, вы видите новые строки: это просто помещается после письменной записи.

Оператор передачи дочерних данных, не позиционирующий файл после завершения, не влияет на новую строку.


На данный момент у меня нет доступа к тестовой машине, поэтому эта часть является предположительной. Вы можете явно написать новый строковый символ, возвращаемый из new_line(''), как часть вывода для дочернего оператора передачи. Поскольку advance='no' будет проигнорирован в дочернем операторе, вы можете использовать это в обоих случаях, явно контролируя, где записываются новые строки, вместо того, чтобы полагаться на подход с разделением записей, который существует в настоящее время.

person francescalus    schedule 26.12.2017
comment
Об этом также спрашивали на форуме Intel. Я ответил, что ввод-вывод производного типа, определяемый пользователем, не является прогрессивным (и вы не можете это изменить). Если вам нужны символы новой строки, вы должны написать их явно (например, используя формат /.) - person Steve Lionel; 26.12.2017
comment
Более краткое утверждение, которое, вероятно, заслуживает здесь ответа, а не комментария, @stevelionel. - person francescalus; 26.12.2017
comment
Ваш ответ был правильным, поэтому я не хотел наступать вам на цыпочки, но если вас это устраивает ... готово! - person Steve Lionel; 26.12.2017
comment
То, что я прав, не означает, что я хорошо это объяснил;). А если серьезно, слишком мало технических вопросов по Фортрану имеет множество объяснений. - person francescalus; 26.12.2017