如何停止 Fortran 列表定向读取以分隔“/”上的字符串

How to stop Fortran list directed read from separating string on '/'

当输入可能包含 / 时,是否可以使用列表定向读取从一行中读取文件路径?它的应用是为我的程序轻松解析来自输入文件的命令。

显示我不想要的行为的示例问题是:

program test

    implicit none

    character(len=100) :: line, label, fname

    line="command file/path"
    read(line, *) label, fname
    print *, fname

end program test

此程序将打印 file 而不是打印 file/path。我需要列表定向输出的原因是因为我希望用户能够在之后提供可选命令。这样的命令输入将是:

command file/path arg1 arg2 

其中可选参数的数量是可变的。如果这不是那么容易做的问题,那么我将实现某种标记化子例程。

这是使用最新版本的 gfortran 的免费形式,如果有区别的话;我不限于任何特定标准。

据我所知,没有专门通过列表定向 IO 的解决方案。但是,您可以通过将输入读取为格式化字符串并将其标记化来解决问题,如您所说,如以下实现,

module String_mod

    use iso_fortran_env, only: IK => int32
    implicit none
    public

    type :: CharVec_type
        character(:)    , allocatable   :: record
    end type CharVec_type

    type :: String_type
        character(:)      , allocatable   :: value          !< The string value.
        type(CharVec_type), allocatable   :: Parts(:)       !< The string parts.
        integer(IK)                       :: nPart = 0_IK   !< The number of parts in the string.
    contains
        procedure, nopass :: split
    end type String_type

contains

    function split(string,delim,npart) result(Parts)

        implicit none
        character(len=*)    , intent(in)            :: string, delim

        integer(IK)         , intent(out), optional :: npart

        type(CharVec_type)  , allocatable           :: Parts(:)
        integer(IK)         , allocatable           :: PartEnd(:)
        integer(IK)         , allocatable           :: PartBegin(:)
        integer(IK)                                 :: dlmlenMinusOne
        integer(IK)                                 :: strlen, dlmlen, npartMax, ipart, ibeg, iend, i
        logical                                     :: npartIsPresent

        dlmlen = len(delim)
        strlen = len(string)
        npartIsPresent = present(npart)

        ! if dlm is empty, return the whole string split character by character

        if (dlmlen==0_IK) then
            allocate(Parts(strlen))
            do ipart = 1, strlen
                Parts(ipart)%record = string(ipart:ipart)
            end do
            if (npartIsPresent) npart = strlen
            return
        end if

        npartMax = 1_IK + strlen / dlmlen ! There can be at most strlen + 1 splits
        allocate(PartBegin(npartMax), PartEnd(npartMax)) ! This will contain the beginning and the ends of the splits.
        dlmlenMinusOne = dlmlen - 1_IK

        ibeg = 0_IK
        ipart = 1_IK
        PartBegin(ipart) = 1_IK
        loopParseString: do

            ibeg = ibeg + 1_IK
            iend = ibeg + dlmlenMinusOne

            if (strlen<iend) then ! the remaining part of the string is shorter than the delim
                PartEnd(ipart) = strlen
                exit loopParseString
            elseif ( string(ibeg:iend) == delim ) then
                PartEnd(ipart) = ibeg - 1_IK
                ipart = ipart + 1_IK
                PartBegin(ipart) = iend + 1_IK
                ibeg = iend
            end if

        end do loopParseString

        allocate(Parts(ipart))
        do i = 1, ipart
            Parts(i)%record = string(PartBegin(i):PartEnd(i))
        end do
        if (present(npart)) npart = ipart

        deallocate(PartBegin, PartEnd)

    end function split
    
end module String_mod

program test

use, intrinsic :: iso_fortran_env, only: output_unit
use String_mod, only: String_type
implicit none
type(String_type) :: string

string%value = "command file/path"
string%Parts = string%split(string = string%value, delim = " ")
write(output_unit,"(*(g0,:,' '))") "fname =", string%Parts(2)%record

end program test

你可以在这里在线测试:https://www.tutorialspoint.com/compile_fortran_online.php 如果您采用这种方法,您将必须向用户明确说明他们必须在输入中使用的定界符的种类。只要文件路径不包含空格,默认选择黑色即可。

在字符项的列表定向输入中,我们可以避免 */,/; 被处理通过分隔它们作为值分隔符。例如,一个/可能出现在一个文件路径中:

program test

    implicit none

    character(len=100) :: line, label, fname

    line="command 'file/path'"
    read(line, *) label, fname
    print *, fname

end program test

回想一下逗号分隔值文件如何要求用逗号将值括起来。

但是,通常您会希望避免使用列表定向输入并使用更高级的输入解析方法。