【问题标题】:Fortran character format string as subroutine argumentFortran 字符格式字符串作为子程序参数
【发布时间】:2018-03-01 13:00:32
【问题描述】:

我正在努力阅读一个文本字符串。我正在使用 gfortran 4.9.2。

下面我写了一个小子程序,我想在其中提交写格式作为参数。

理想情况下,我希望能够调用它

call printarray(mat1, "F8.3")

例如以该格式打印出矩阵 mat1。列数应在子程序内自动确定。

subroutine printarray(x, udf_temp)
implicit none
real, dimension(:,:), intent(in) :: x           ! array to be printed     
integer, dimension(2)            :: dims        ! array for shape of x
integer                          :: i, j
character(len=10)                :: udf_temp    ! user defined format, eg "F8.3, ...
character(len = :), allocatable  :: udf         ! trimmed udf_temp
character(len = 10)              :: udf2
character(len = 10)              :: txt1, txt2
integer                          :: ncols       ! no. of columns of array
integer                          :: udf_temp_length

udf_temp_length = len_trim(udf_temp)
allocate(character(len=udf_temp_length) :: udf)

dims = shape(x)
ncols = dims(2)
write (txt1, '(I5)') ncols
udf2 = trim(txt1)//adjustl(udf)
txt2 = "("//trim(udf2)//")"

do i = 1, dims(1)
   write (*, txt2) (x(i, j), j = 1, dims(2))        ! this is line 38
end do

end suroutine printarray

当我设置len = 10:

character(len=10) :: udf_temp

我得到编译错误:

call printarray(mat1, "F8.3")
                      1
Warning: Character length of actual argument shorter than of dummy argument 'udf_temp' (4/10) at (1)

当我设置len = *

character(len=*) :: udf_temp

它编译但在运行时:

At line 38 of file where2.f95 (unit = 6, file = 'stdout')
Fortran runtime error: Unexpected element '(    8

我做错了什么? 有没有更简洁的方法来做到这一点?

【问题讨论】:

  • 当 len=10 时的调试输出在文件 where2.f95 (unit = 6, file = 'stdout') 的第 38 行 Fortran 运行时错误:Unexpected element '( 8 [Inferior 1 (process 19607) exited代码 02] len=* 时的调试输出 where2.f95 (unit = 6, file = 'stdout') Fortran 运行时错误:意外元素'( 8 [Inferior 1 (process 19634) exited with code 02]
  • 不要把你想让人们理解的材料放入 cmets,格式不够好。
  • 您实际上并没有在任何地方使用 udf_temp - 投票关闭只是一个简单的错字。如果你能解释一些真正的误解,我可能会撤回投票。
  • 此外,您在引用它之前没有定义udf(在udf2 = trim(txt1)//adjustl(udf) 中)..
  • 令人费解的是,"8" 是如何出现在错误消息中的。我认为显示的代码并不是真正给出该错误的原因。

标签: fortran gfortran


【解决方案1】:

这是我将尝试解决的问题的摘要:您希望有一个子例程来打印具有指定格式的指定二维数组,这样每一行都打印在一行上。例如,假设我们有真正的数组:

real, dimension(2,8) :: x
x = reshape([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], shape=[2,8], order=[2,1])

! Then the array is:
!    1.000   2.000   3.000   4.000   5.000   6.000   7.000   8.000
!    9.000  10.000  11.000  12.000  13.000  14.000  15.000  16.000

我们希望使用"F8.3" 格式,它打印字段宽度为 8 位和 3 位小数的浮点值(实数)。

现在,您在子例程中创建格式时犯了几个错误。首先,您尝试使用udf 创建udf2 字符串。这是一个问题,因为尽管您分配了udf 的大小,但没有为其分配任何内容(@francescalus 在评论中指出)。因此,您会看到您报告的错误消息:Fortran runtime error: Unexpected element '( 8

在下文中,我进行了一些简化更改并演示了一些(略微)不同的技术。如图所示,我建议使用* 来表示该格式可以无限次应用,直到输出列表的所有元素都被访问过。当然,明确说明应用格式的次数(即"(8F8.3)" 而不是"(*(F8.3))")也可以,但后者的工作量略少。

program main
    implicit none

    real, dimension(2,8) :: x
    character(len=:), allocatable :: udf_in

    x = reshape([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], shape=[2,8], order=[2,1])
    udf_in = "F8.3"
    call printarray(x, udf_in)

contains
    subroutine printarray(x, udf_in)
        implicit none
        real, dimension(:,:), intent(in) :: x
        character(len=*), intent(in)     :: udf_in

        integer                        :: ncols         ! size(x,dim=2)
        character(len=10)              :: ncols_str     ! ncols, stringified
        integer, dimension(2)          :: dims          ! shape of x
        character(len=:), allocatable  :: udf0, udf1    ! format codes
        integer                        :: i, j          ! index counters

        dims = shape(x)                                 ! or just use: ncols = size(x, dim=2)
        ncols = dims(2)

        write (ncols_str, '(i0)') ncols                 ! use 'i0' for min. size

        udf0 = "(" // ncols_str // udf_in // ")"        ! create string: "(8F8.3)"
        udf1 = "(*(" // udf_in // "))"                  ! create string: "(*(F8.3))"

        print *, "Version 1:"
        do i = 1, dims(1)
            write (*, udf0) (x(i, j), j = 1,ncols)      ! implied do-loop over j.
        end do

        print *, "Version 2:"
        do i = 1, dims(1)
            ! udf1: "(*(F8.3))"
            write (*, udf1) (x(i, j), j = 1,ncols)      ! implied do-loop over j
        end do

        print *, "Version 3:"
        do i = 1, size(x,dim=1)                         ! no need to create nrows/ncols vars.
            write(*, udf1) x(i,:)                       ! let the compiler handle the extents.
        enddo

    end subroutine printarray
end program main

注意:最后的 do-loop(“版本 3”)非常简单。它不需要明确的 ncols 计数,因为 * 会自动处理它。由于其简单性,实际上根本不需要子程序。

【讨论】:

    【解决方案2】:

    除了实际的错误(不使用输入参数),整个事情可以更简单地完成:

      subroutine printarray(m,f)
      implicit none
      character(len=*)f
      real m(:,:)
      character*10 n
      write(n,'(i0)')size(m(1,:))
      write(*,'('//n//f//')')transpose(m)
      end subroutine
      end
    

    注意不需要循环结构,因为 fortran 会在达到格式指定的数据长度时自动写入整个数组,换行。

    或者,您可以使用循环构造,然后您可以在格式中使用“*”重复计数,而无需内部写入来构造格式字符串。

      subroutine printarray(m,f)
      implicit none
      character(len=*)f
      real m(:,:)
      integer :: i
      do i=1,size(m(:,1))
         write(*,'(*('//f//'))')m(i,:)
      enddo
      end subroutine
      end
    

    【讨论】:

    • 哎呀,我想我们的答案几乎相同,但你打败了我。哦,好吧。
    猜你喜欢
    • 2022-11-22
    • 1970-01-01
    • 2018-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多