如何停止 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
回想一下逗号分隔值文件如何要求用逗号将值括起来。
但是,通常您会希望避免使用列表定向输入并使用更高级的输入解析方法。
当输入可能包含 /
时,是否可以使用列表定向读取从一行中读取文件路径?它的应用是为我的程序轻松解析来自输入文件的命令。
显示我不想要的行为的示例问题是:
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
回想一下逗号分隔值文件如何要求用逗号将值括起来。
但是,通常您会希望避免使用列表定向输入并使用更高级的输入解析方法。