跳转到内容

Fortran/io

来自维基教科书,开放世界中的开放书籍

在 Fortran 和其他语言中,指定想要打印或读取的内容的位置和方式通常很有用。Fortran 提供了许多命令和格式规范,可以满足这些目的。在以下部分,我们将考虑 I/O 操作(opencloseinquirerewindbackspaceendfileflushprintreadwritenamelist)和 I/O 格式(format)。在 Fortran 2008 中,一个重要的补充是能够使用用户定义的例程扩展基本功能,以输出派生类型,包括那些本身由其他派生类型组成的类型。

这些命令共同构成了一个非常强大的功能集,用于读取和写入格式化和非格式化文件,这些文件具有顺序、直接或异步访问方式。实际上,这些选项乍一看可能令人困惑。但是,基本操作很简单,但它们以强大的灵活性和读取或写入几乎所有文件的强大功能为后盾。

但是,值得一提的是,在语言定义的角度来看,Fortran 不能简单直接地执行的操作:Fortran 不会处理由 URL 定义的文件,文件必须在本地或映射的网络驱动器或等效位置可用。类似地,Fortran 本身不支持 XML,除了 XML 是简单的 ASCII 文本,因此可以轻松读取,但解析它则由程序员负责!Fortran 语言不知道计算机图形;你不会在语言定义的命令中找到 draw 命令。Fortran 很乐意打开并读取 jpg 文件,但没有语言定义的方法将文件显示为图片;必须使用合适的外部库。最后,Fortran 没有任何语言定义的鼠标操作或触摸屏手势。

I/O 操作

[编辑 | 编辑源代码]

现代 Fortran 拥有丰富的 I/O 操作词汇。这些操作通常可以在屏幕和键盘、外部文件和内部文件中使用。在最近的 Fortran 版本中,这些命令的语法已经变得更加合理,但为了向后兼容,大部分原始语法都保留了下来。I/O 操作是出了名的容易出错,Fortran 现在支持统一的机制来识别和处理错误。

简单 I/O 操作

[编辑 | 编辑源代码]

这是经典的“Hello World”操作,但在生产代码中很少使用。print 是两种格式化输出操作之一,它也比 write 语句简单得多。print 语句的主要目的是打印到屏幕(标准输出单元),它没有文件输出选项。一般形式是

print fmt, list

fmtlist 都是可选的,fmt 可以是显式或列表导向(如 * 所示),并且是可选的,可以采用 name=value 格式。列表是文字或内在类型变量的逗号分隔列表。以下是一些示例

program hello
    implicit none
    integer :: i

    ! List-directed fmt
    print *, "Hello World"
    do i = 1, 10
        ! An explicit fmt and a two element list
        print '(A,I0)', "Hello ", i
    end do
    ! Name=value for fmt
    print fmt='(A)', 'Goodbye'
end program hello

请注意,print 几乎是唯一仍然不支持 iostatiomsg 子句的 I/O 操作,仅此原因就应该在新代码中不使用它,除非用于临时输出和调试。print 没有用于打印用户定义类型的显式机制,这也是在生产代码中不使用它的另一个原因。

I/O 通道和文件

[编辑 | 编辑源代码]

在上面显示的 print 示例中,print 语句自动预连接到标准输出设备,也称为计算机屏幕。但是,通常情况下,Fortran 需要一个两阶段过程才能将代码连接到外部文件。首先,我们必须将文件连接到 Fortran 通道(手动通过正整数标识或自动分配负值):open 命令,然后我们可以对现在打开的通道进行 readwrite 操作。对未打开的通道进行 readwrite 操作会导致错误;在相关通道打开之前,没有语言指定的缓冲区。完成 I/O 操作后,我们可以关闭文件和 Fortran 通道之间的连接。如果 Fortran 程序在通道关闭到文件之前终止,Fortran 通常会关闭通道,而不会丢失大量数据。

可以通过 inquire 命令的一种形式来确定 Fortran 通道的可用状态。inquire 命令还可以在将文件连接到 Fortran 通道之前用于确定文件的存在和其他属性。

Fortran 对内部文件的 I/O 不需要预连接过程。Fortran 从键盘输入和输出到屏幕的 I/O 在特殊通道 (*) 上自动预连接。编译器供应商可以自由地将通道号分配给这些标准 I/O 设备,用户可以通过内在模块 iso_fortran_env 确定已使用哪些通道号。

这是建立外部文件和 Fortran 通道之间连接所需的命令。open 命令可用于创建新文件或连接到现有文件。打开后,对文件的后续 I/O 是通过此通道号进行的。open 命令具有选项来确保文件已存在或不存在,以确保它仅用于输入,或仅用于输出,或用于两者。可以在 open 命令中指定文件的预期格式,并可以捕获错误。命令的完整语法可能看起来很复杂,但通常任何一次对 open 的调用只使用所有可用选项中的一个小子集。

open 命令最初用于外部文件通常是卡片图像的时候。open 现在可以指定文件连接为固定格式、异步、二进制流以及这些格式的多种组合。值得记住的是,I/O 是潜在编码错误的主要来源,在读取关键数据时,应将其重新写入以确认处理正确。

Fortran 通道号的值在任何一个 Fortran 程序的图像中都有全局作用域。即使使用整型变量打开通道,并且该变量的作用域非常有限,但实际的通道号实际上无处不在。在设计时需要考虑这一点,因为一个模块可以打开,比如通道 10,另一个模块可以关闭通道 10,而它们之间没有任何使用关联。出于这个原因,在大型程序中,通常使用单个模块来控制所有文件 I/O 操作,以便可以维护清晰明显的“打开 - 读取/写入 - 关闭”链。

最后,和往常一样,Fortran 有些选项和子句是为了遗留目的而保留的,不应该在新代码中使用。

打开命令语法

[edit | edit source]
open ([unit=]u[, olist])

其中 [] 表示可选部分,olist 是一个用逗号分隔的选项列表。在上面,u 是一个标量整型表达式或等效的,并且是必需的,除非指定了 newunit 选项。(不幸的是,我们不能在一个 open 语句中打开多个文件)。从技术上讲,u 被称为外部文件单元号,简称通道号。

常用选项

[edit | edit source]

newunit=nu,其中 nu 是一个默认的整型变量。这允许处理器选择通道号,并且,为了避免与遗留代码冲突,将选择一个负值(不是 -1),它不会与任何当前正在使用的单元号冲突。这应该是所有新代码中使用的形式。

iostat=ios,其中 ios 是一个默认的整型变量,如果 open 语句没有检测到错误,则将被设置为零,但如果发生错误,则将被设置为正值,并且确切的值取决于供应商。虽然从技术上讲是一个选项,但这是所有 open 命令的强烈推荐选项。如果没有此选项(并且没有 err= 选项,请参见下文),如果发生错误,程序将停止。此选项的存在使程序员有责任检查返回值,并让程序相应地采取行动。

iomsg=iom,其中 iom 是一个默认类型的标量字符变量。同样,虽然从技术上讲是一个选项,但这对于新代码中的所有 open 命令来说都是一个强烈推荐的选项。消息的长度是错误和供应商特定的,可能需要一些试错。

file=fln,其中 fln 是一个默认的字符变量、文字或表达式,用于指定外部文件的名称。文件名可以是完全限定的路径或本地文件名。如果路径指向网络驱动器上的文件,则驱动器必须预先连接,并且没有语言定义的方法来建立这种连接。(除了我们可以始终诉诸于 execute_command_line

status=stn,其中 stn 也是一个默认的字符变量、文字或表达式,它必须评估(不区分大小写)为“old”、“new”、“replace”、“scratch”或“unknown”之一。“old”要求文件存在,通常在 open 语句的目的是允许读取文件时使用。“new”和“replace”要求存在上面描述的 file= 选项,“new”要求文件不存在,“replace”允许文件已经存在,但如果存在,则将被覆盖。“scratch”很特殊,因为它不能使用 file= 选项,并且在随后执行 close 命令时不能保留创建的文件。“scratch”通常用于将大型数据结构临时存储到硬盘或类似的大容量存储设备中。如果指定了“unknown”,这也是 status= 选项未给出的默认值,并且文件状态变得依赖于供应商和系统,即需要查阅手册。

action=act,其中 act 是一个默认类型的字符表达式、变量或值,它评估为“read”、“write”或“readwrite”。令人惊讶的是,默认值是处理器相关的,因此需要查阅手册。如果指定了“read”,则该文件将被视为只读,并且尝试在此通道上执行 writeprintend file 语句会导致错误。类似地,如果指定了“write”,则该文件将被视为只写,并且尝试执行 read 语句会导致错误。当指定“write”时,某些其他语句可能会以处理器相关的方式导致错误(例如 backspace)。

打开的简单示例

[edit | edit source]
program opena
    implicit none
    integer :: nout !channel number
    integer :: my_iostat !integer scalar to catch error status
    character (len=256) :: my_iomsg !Default-kind character variable to catch error msg

    open (newunit=nout, file="local.dat", iostat=my_iostat, iomsg=my_iomsg)
    if (my_iostat /= 0) then
        write (*,*) 'Failed to open local.dat, iomsg='//trim(my_iomsg)
        stop
    end if
    write (nout,*) 
end program

不太常用的选项

[edit | edit source]

access=acl,其中 acl 是一个字符表达式、变量或文字,它评估为“sequential”、“direct”或“stream”之一。当打开已经存在的文件时,此值必须与允许的值对应,该值通常是在创建文件时给出的值。新文件的默认值为“sequential”。“stream”访问是 Fortran 2008 中的新功能,它提供了一些与 C 二进制流文件兼容性。“stream”访问的另一个真正重要的特征是,可以将文件定位为写入,并且可以覆盖文件的一部分,而不会改变文件的其余部分。对于格式化的流文件,new_line(nl) 函数将在字符变量 nl 中返回相关的新行字符。

recl=rcl,其中 rcl 是一个整型表达式、变量或文字,它必须评估为正值。对于要以直接访问方式打开的文件,此“选项”是必需的,并且必须指定每个记录的长度。对于顺序文件,它是可选的,可以用来指定记录的最大长度。对于已经存在的文件,rcl 的值必须与用于创建文件的那个值相对应。在任何情况下,rcl 的值也必须被底层操作系统允许。

form=frm,其中 frm 是一个字符表达式、变量或文字,它评估为“formatted”或“unformatted”之一。此选项通常可以省略,因为顺序访问的默认值为“formatted”,直接访问的默认值为“unformatted”。

blank=blk,其中 blk 是一个字符表达式、变量或文字,它为格式化 I/O 提供“null”或“zero”的值。请参见下面的 bn 和 bz 格式。

position=psn,其中 psn 是一个字符表达式、变量或文字,它评估为“asis”、“rewind”或“append”,并且仅在访问方法为顺序时适用。默认值为“asis”。当打开时,新文件始终定位在其初始点,但对于现有文件,用户可以选择当前位置的定位位置。

delim=

pad=

要避免的选项

[edit | edit source]

那里有很多遗留代码,本节描述了仍然合法的功能,但应该考虑替换它们,当然不能在新代码中使用。

err=eno,其中 eno 是一个文字整型标签号。如果在处理 open 语句时发生错误,程序将控制权转移到标签号为 eno 的语句。err= 选项的存在导致程序在发生错误时继续。此选项现在应该用 iostat=iomsg= 替换。(在 err=iostat= 中指定两者都是合法的,但如果没有两者,如果在处理 open 语句时发生错误,程序将停止。)

unit=nu,其中 nu 是一个默认的整型表达式、变量或文字值,它必须为正,并且不能与任何已使用的单元重合。如果此选项放在选项列表中的第一个位置,“unit=”可以省略。这曾经被广泛使用,现在应该用 newunit= 替换。在非常旧的代码中,nu 是一个固定值,程序员必须确保它不会与同时使用的其他通道发生冲突。最近,可以使用 inquire 语句选择尚未使用的单元号,但这不能防止后续的 open 语句尝试使用已使用的固定值。这就是为什么 newunit 选项(并且只有 newunit 选项)允许为通道号指定负值。

读取

[edit | edit source]

read 语句是一个从指定输入以指定格式读取数据的语句,一个变量。例如,

program reada
    implicit none
    integer :: a

    read (*,*) a
end program

将创建一个用于 a 的整型内存单元,然后它将使用默认格式从默认输入读取一个值并将其存储在 a 中。(*,*) 中的第一个 * 表示应该从哪里读取值。第二个 * 指定用户希望使用的读取数字的格式。让我们首先讨论可用的格式字符串。在 Fortran 中,我们可以使用许多格式字符串来指定我们希望数字或字符字符串在屏幕上显示的方式。对于定点实数:Fw.d;w 是分配给数字的总空格数,d 是小数位数。小数点始终占一个位置。例如,

program reada
    implicit none
    real :: a

    read (*,'(F5.2)') a
end program

I/O 格式的详细信息(例如,'(F5.2)')将在下面描述

写入

[edit | edit source]

查询

[edit | edit source]

inquire 语句有两种基本形式:“按单元inquire”和“按文件inquire”,这两种形式都非常有用,值得学习。还有一种不太常用的形式,称为“按长度inquire”,它用于检查潜在输出的非格式化记录长度,以便确定需要什么记录长度,或者已经定义了记录长度的文件是否可以处理给定的输出。

按单元查询

[编辑 | 编辑源代码]

按文件查询

[编辑 | 编辑源代码]

按长度查询

[编辑 | 编辑源代码]

这种不太常用的 inquire 命令版本用于获取包含给定形式输出所需的非格式化记录长度,从而允许用户检查或指定所需的记录长度。

查询错误

[编辑 | 编辑源代码]

close 命令释放文件和 Fortran 通道之间的连接。在此过程中,根据文件的打开方式,可以保存或丢弃文件。close 命令的一般形式如下

close ([unit=]u, [, olist])

其中 u 是一个默认的整型表达式、变量或文字值,它计算为要关闭的通道的编号,unit= 是可选的。选项列表中可用的选项如下

iostat=ios,其中 ios 是一个默认的整型变量,如果 close 命令执行正确,它将返回 0。如果发生错误,返回值将为正数,并将通过下面描述的 iomsg 选项提供描述错误的消息。

err=eno,其中 eno 是一个文字整型标签号。如果在处理 close 语句时发生错误,程序将控制权转移到标签号为 eno 的语句。err= 选项的存在导致程序在发生错误时继续执行。现在,此选项应替换为 iostat=iomsg=。(在技术上允许指定 err=iostat=,但如果没有指定任何一个,如果在处理 open 语句时发生错误,程序将停止。)

iomsg=iom,其中 iom 是一个默认类型的标量字符变量。同样,尽管从技术上讲是一个选项,但对于新代码中的所有 close 命令来说,这是一个强烈推荐的选项。消息的长度是错误和供应商特定的,可能需要一些试错。

status=st

关闭错误

[编辑 | 编辑源代码]

可能有点违反直觉,但 Fortran 不认为尝试关闭已经关闭的通道是错误。与 inquireopen 一样,close 将报告的错误实际上是操作系统错误。例如,如果通道已关闭,则使用 status="delete" 关闭通道是一个错误,尤其是在文件不再存在的情况下。同样,如果以 'scratch' 创建的文件使用 status="keep" 关闭,Fortran 将报告错误,因为暂存文件的唯一选项是 status='delete'。这些限制基于使用 iostat(和 iomsg)子句允许用户在必要时对程序进行优雅的终止,而不是获得有关 close 命令执行情况的完整报告。

I/O 格式化

[编辑 | 编辑源代码]

列表定向格式化

[编辑 | 编辑源代码]

我们在下面描述了显式格式化,但很明显,存在一种“语言中的语言”,因此 Fortran 提供了一种快捷方式,或者语言定义的格式猜测。事实证明,这种默认格式非常接近用于输入的逗号分隔值 (CSV) 处理器。列表定向 I/O 由 fmt=* 子句指定,但 fmt 子句是可选的,可以替换为 *。

显式格式化

[编辑 | 编辑源代码]

Fortran 拥有一种丰富但非常简洁的语言,用于控制 I/O 操作的格式。格式命令可以放置在显式 format 语句中,也可以放置在相关 READ 或 WRITE 语句的子句中,无论是直接放置还是存储在 character 变量中。

华夏公益教科书