文件的存取
7.2.1 逻辑设备和文件
a) 逻辑设备
在Fortran中对文件和外部设备的操作都是通过逻辑设备进行的。在对文件和外部设备进行操作之前,都要把它们连接到逻辑设备上。内部文件的设备描述符和外部文件的设备描述符是不同的:内部文件用一个字符变量或其它变量名来描述,外部文件用OPEN语句打开文件时的数字(单元号)作为文件的描述,或是用默认的设备单元号包括星号(*)作为文件的描述。对于一个设备描述符,不能同时连接一个以上文件同样,—个文件也不能同时与一个以上设备描述符连接。在F90中,可以用OPEN语句打开一个已经打开的文件,但是通过这种方式打开的文件只能对这个文件的输入输出属性选项进行修改,而不能对其进行输入输出操作。
除下列三种情况,必须在每个输入输出语句中使用设备描述符:
1) 使用PRINT进行输出,PRINT语句的输出是把数据输出到标准输出设备上(单元号6,即屏幕)。例:PRINT I
2) 只包含一个I/O列表和格式描述符的READ语句以及名称列表READ语句,其形式为:READ 格式描述符 [,I/O列表]和 READ名称列表,它是从标准输入设备上(单元号5,即键盘)输入数据的。
3) 对文件按文件名进行的INQUIRE操作,这时查询的参数是文件名,而不是连接着文件的设备号。
b) 外部文件
根据所操作的文件是否在内存中可以把文件分为内部文件和外部文件。当把内存中的数据记录到到磁盘的文件中或输入输出到其他外部设备如打印机、显示器、键盘上时,被称为外部文件。连接着一个外部文件的设备描述必须是—整型表达式或是星号(*),其整型表达式的取值范围在-32768到32767之间。
例:OPEN(UNIT=10,FILE=’output.dat’)
WRITE(10,’(A)’) ’how are you? ’
在Fortran中有4个预定义的外部文件(设备):
设备号 | 连接的设备 |
星号(*) | 总是键盘和显示器 |
0 | 缺省状态下是键盘和显示器 |
5 | 缺省状态下是键盘 |
6 | 缺省状态下是显示器 |
不能关闭星号设备号。设备号0,5,6可以通过OPEN语句连接到其他文件上,当在程序中关闭设备号是O,5,6的外部文件后,如果下次还要使用这些设备号进行输入输出操作,这些设备号将自动连接到它们各自的缺省的设备上去。
c) 内部文件
内存中的数据也可以像磁盘上的文件一样进行操作。把连接到设备描述符上的、进行与文件相类似的操作的一块内存中的数据称为内部文件。连接着一个内部文件的设备描述符是—个字符串或是一个字符数组。使用内部文件的规则是:对内部文件只能使用格式化的输入输出操作,包括用格式描述符和直接列表来限定格式的输入输出操作。只能用READ和WRITE语句对内部文件进行操作,不能用文件连接(OPEN)、文件指针位置的设置(REWIND,BACKSPACE)或是文件属性查询(INQUIRE)语句。
利用内部文件及输入输出系统的格式化功能,可以实现数据在外部的字符表示和在内存表示之间的转换。即可以通过从—个内部文件中读取数据到—个变量或数组中实现ASCII的存储方式到数字、逻辑或字符等内存存储方式的转换,或者是通过把—个变量写到内部文件中,实现内存存储方式到ASCII存储方式的转换。
在进行内部文件的写操作时,当写入的内容长度小于内部文件的一个记录的长度时,记录中剩余的空间被空格填满;大于时多余的数据将被删除。在进行内部文件的读操作时,当内部文件的一个记录的长度小于希望读取的数据长度时,没有读取到数据的变量填为空格;大于时多余的内部文件数据被删除。
例:CHARACTER(10) str
CHARACTER(14) fname
str = " 1 2 3"
READ(str,*) n1,n2,n3 !直接列表READ语句设置 n1=1, n2=2, n3=3
i=4
WRITE(fname,200) i !格式化WRITE语句设置fname ='FM004.DAT'.
200 FORMAT('FM',I3.3,'.DAT')
7.2.2 外部文件分类
Fortran支持两种文件的访问方式(顺序访问和直接访问)和三种文件的结构(有格式、无格式、二进制)。顺序访问或直接访问可以用于这三种结构的文件进行的每一种。因此,共有6种文件类型。
a) 格式化文件
在格式化文件中,记录数据内容的记录是以ASCII字符的方式存在的,每一条记录是以ASCII码中的回车符CR(0D)加换行符LF(0A)来结束的,可以用文本编辑软件打开格式文件并直接看懂其内容。即存放在文件中的数字就是平时所看到的数字字符,字符串也就是平时所看到的字符串。而若用文本编辑软件打开无格式文件或二进制文件,看到的则是一些十六进制的字符。因此如果要使文件中的内容可以被人直接看懂,应用格式文件。
OPEN语句默认的打开文件是格式文件,也可以使用FORM=‘FORMATTED’设置项的OPEN语句以明确文件是格式化的。
b) 无格式文件
无格式文件由一系列物理块组成的记录组成,所存储的记录序列的存放方式与其在内存中的存放非常相似,所以在输入输出时几乎不需作转化。由于去掉了格式控制,与有格式文件相比,在使用数据信息时所做的处理更简洁更迅速;同样也是这个原因使得无格式文件中即使存放着数字,也不能用文本编辑软件打开并看到它们。
使用无格式文件之前,应该先打开或建立一个无格式文件。通过带有FORM=‘UNFORMATTED’设置项的OPEN语句来打开或建立一个文件,或者用省略FORM选项的OPEN语句来打开或建立一个无格式直接访问文件。
c) 二进制文件
二进制文件是处理最快、最简洁的一种文件,也是最紧凑的存储格式,适合于大批量数据的存储。在程序中可以用带有FORM=’BINARY’选项的OPEN语句来打开或建立二进制文件。
d) 顺序访问文件
存放在顺序文件中的数据必须一个记录接一个记录地按顺序被访问。也就是说,程序中要读写第N条记录时,必须至少已对前面的N-1记录进行过读操作。在输入输出操作中,有些方法只有在顺序访问的文件中才可能实现,包括低级的输入输出操作、直接列表和名称列表输入输出操作。内部文件也必须使用顺序文件。键盘、显示器和打印机等顺序访问的外部设备必须连接成顺序文件。OPEN语句默认的打开文件是顺序文件,也可以使用ACCESS=‘SEQUENTIAL’设置项的OPEN语句以明确文件是顺序文件。
当对顺序文件进行输出时,在OPEN语句之后总是把WRITE语句输出的记录作为文件的开头,当前的WRITE语句所输出的记录总作为文件的最后一条记录。如果所写的顺序文件是一个已经存在的文件,则文件原来的内容将全部丢失。对一个顺序文件在读操作后立即进行写操作,则当前写的这个记录就成了文件的最后一个记录,在写操作后立即进行读操作,则必然遇到文件结束记录。如对同一个文件写操作后要进行读操作时,必须使用能够对文件的指针进行重定位的语句重新设置文件的指针。
e) 直接访问文件
存放在直接访问文件中的记录可以以任意顺序进行读写操作。文件中的记录从1开始连续编号,记录的长度是通过OPEN语句中的RECL选项来描述的。直接文件中的记录是通过指定要访问的记录号来实现的。因此,如果想要实现数据的随机访问可以使用直接访问文件。直接文件应用的一个最常见的实例就是数据库。在程序中可以用带有ACCESS=’DIRECT’设置项的OPEN语句来打开或建立直接文件。
直接文件中的每个记录的长度必须相等。如果实际输出的记录长度不等,则应取输出的所有记录中最大的长度作为每个记录的长度。如果使用一个老文件,在OPEN语句中说明的记录的长度必须与实际的记录长度一致。特别要注意尾随的空格符,它占一个字节。回车换行符不计入记录长度。用直接方式建立的文件可以使用顺序方式打开进行读操作。用顺序方式建立的文件,只要记录长度相等,也可以用直接方式打开进行读操作。
7.2.3 文件记录的存取
a) 格式化顺序文件
—个格式化文件是一个由按顺序写到文件中的有格式记录序列组成的,当要对文件进行读操作时,读取的顺序就是记录在文件中的存放顺序。文件中记录的长度不一定相同,记录也可以是空的。记录用回车符(0DH)和换行符(0AH)分开。
0A
记录N
0D
0A
记录N+1
0D
例:OPEN(3,FILE=’FSEQ’)
! FSEQ is a formatted sequential fife by default.
WRITE(3,’(A,I3)’) ’RECORD’,1
WRITE(3, ’()’)
WRITE(3, ’(A11) ’) ’The 3rd one’
CLOSE(3)
END
RECORD 1
0D
0A
0D
0A
The_3rd_one
0A
0D
记录1中的9个字节
2字节
分隔符
2字节
分隔符
2字节
分隔符
记录3的11字节
记录2中的0字节
b) 格式化直接文件
在格式化直接文件中,所有记录的长度都相同并且可以以任意顺序读写。记录的长度由OPEN语句中的RECL=选项指定,该长度应该大于或等于最长的记录中的字节数。CR和LF是分隔符,不包括在RECL中。一旦某个直接访问记录被写入就不能再删除它,但可以覆盖这个记录。在输出到一个格式化直接文件时如果数据没有占满一个记录,则编译系统将在剩下的位置上补以空格,保证文件只包含长度相同的完整的记录。从文件中读数据时,当I/O列表或格式描述符中要读取的数据多于记录中的数据时,编译器也会以空格填充未读数据的变量。可以通过在打开文件的OPEN语句中设置PAD=NO来避免填补空格,此时输入记录必须有和输入列表和格式描述符所要求的一样多的数据,否则会产生错误。PAD=NO对输出没有影响。
例:OPEN(3,FILE=’FDIR’,FORM=’FORMATTED’,ACCESS=’DIRECT’,RECL=10)
WRITE(3,’(A10)’,REC=1) ’RECORD ONE’
WRITE(3,’(I5)’,REC=3) 30303
CLOSE(3)
END
c) 无格式顺序文件
无格式顺序文件中记录的长度可以不同,文件以130或少于130字节为一个物理块进行组织。每个物理块包含着输入到文件中的数据(最多128字节),编译系统在物理块之间加入两个1字节长的长度值以说明每个记录的起始和结束位置。
一个逻辑记录包含一个或多个物理块,其大小可在程序中指定,编译系统会相应地使用需要数量的物理块。当创建一个包含多个物理块的逻辑记录时,编译系统把长度值置为129以表示当前物理块的数据和下一个物理块相连接。例如,如果写入了140字节的数据,则逻辑记录的结构如图所示。
129
128字节数据
129
12
12
12字节数据
一个逻辑记录
无格式顺序文件中的第一个和最后一个字节是保留字节:第一个字节的值为75,最后一个字节的值为130。Fortran使用这些字节作为错误检测和文件结束的判断。
例:CHARACTER xyz(3)
INTEGER(4) idata(35)
DATA idata/35*-1/,xyz/’x’, ’y’, ’z’/
OPEN(3,FILE=’USEQ’,FORM=’UNFORMATTED’)
WRITE(3) idata
WRITE(3) xyz
CLOSE(3)
END
产生的数据文件结构如下,但是在Visual Fortran中需要加上编译指定参数/fpscomp,可在Fortran Compatibility选项框中选取I/O Format项,否则文件的结构是不同的。[e_7_2_3.f90]
d) 无格式直接文件
无格式直接文件是一系列非格式的记录,可以以任意顺序读写记录。记录的长度都相同,由OPEN语句中的RECL=选项指定。没有字节分隔符或其它表示记录结构的字节。
例:OPEN(3,FILE=’UDIR’,RECL=10,FORM=’UNFORMATTED’,ACCESS=’DIRECT’)
WRITE(3,REC=3) .TRUE., ’abcdef’
WRITE(3,REC=1) 2049
CLOSE(3)
END
01080000
(Hex)
FFFFFFFF
(Hex)
abcdef
(ASCII)
记录1
记录2
1
4
5
10
11
20
21
24
25
30
e) 二进制顺序文件
二进制顺序文件是一系列按同一顺序和同样二进制数个数来读写的值。其中没有记录边界,没有说明文件结构的特殊字节。数据读写时长度和形式都不改变。数据记录的长度可以不等。对于任何输入输出数据,内存中的字节序列就是文件中的字节序列。二进制顺序文件是处理最简洁、速度最快的文件。
例:INTEGER bells(4)
CHARACTER(4) wys(3),cvar
DATA bells/4*7/,cvar/’ is’/,wys/’What’,’ you’,’ see’/
OPEN(3,FILE=’BSEQ’,FORM=’BINARY’)
WRITE(3) wys,cvar
WRITE(3) ’what’, ’ you get!’
WRITE(3) bells
CLOSE(3)
END
What_you_see
(ASCII)
is
(ASCII)
what
(ASCII)
you get! (ASCII)
07 07 07 07
(ASCII)
1
12
13
16
17
21
22
29
30
45
先写入16字节
然后写入13字节
最后写入16字节(响铃字符)
f) 二进制直接文件
二进制直接文件存储一系列二进制数记录,它们可以按任何顺序访问。与二进制顺序文件不同的是,这些记录的长度是相等的,由OPEN语句中的RECL=选项指定。在二进制直接文件中可以写入部分记录,记录中末使用的部分将以未定义数据填充。
在二进制直接文件中可以只使用一条读或写语句来读写到多于一条的记录,而这样的操作在无格式直接文件中将引发错误。在无格式直接文件中所能进行的一切操作在二进制直接文件中都是合法的,另外,在二进制直接文件中提供了一种不依赖于填充二进制数据O的对记录的某部分进行读写操作的功能。二进制直接文件是所有6类文件中使用最灵活的一类。
例:OPEN(3,FILE=’BDIR’,RECL=10,FORM=’BINARY’,ACCESS=’DIRECT’)
WRITE(3,REC=1) ’abcdefghijklmno’
WRITE(3) 4,5
WRITE(3,REC=4) ’pq’
CLOSE(3)
END
abcdefghij
(ASCII)
klmno
(ASCII)
0400000005000000
(Hex)
pq
(ASCII)
记录1
记录2
记录3
记录4
1
11
15
20
21
29
30
34
10
16
28
31
32
33
7.2.4 文件操作语句
a) OPEN语句
OPEN语句用于把设备号与文件名连接起来,并且对文件的各项性质进行指定。它的一般形式为:
OPEN([UNIT=]unit[,ACCESS=access][,ACTION=action][,BLANK=blanks][,BLOCKSIZE=blocksize][,CARRIAGECONTROL=carriagecontrol][,DELIM=delim][,ERR=err][,FILE=file][,FORM=form][,IOFOCUS=iofocus][,IOSTAT=iostat][,PAD=pad][,POSITION=position][,RECL=recl][,SHARE=share][,STATUS=status])
其中的各项参数的意义及取值如下:
1) UNIT:设备号说明。unit是大于或等于0的正整数,设备号说明是OPEN语句的第—项时可以省略“UNIT=”。
2) ACCESS:存取方式说明。access是字符串表达式:
APPEND 追加方式
SEQUENTIAL 顺序访问方式
DIRECT 直接访问方式
当省略此说明项时为顺序访问方式。
3) ACTION:描述文件的读写属性。action是字符串表达式:
READ 文件为只读方式打开
WRITE 文件为只写方式打开
READWRITE 文件为可读写方式打开
当省略此说明项时,文件打开顺序:READWRITE->READ->WRITE。
4) BLANK:说明数据格式输入字段中空格的含义。blank是字符串表达式:
NULL 空格忽略不计,相当于在格式描述符中的BN编辑符
ZERO 空格处理成数字0,相当于BZ编辑符
当省略此说明项时为ZERO。此说明只能用于格式输入。
5) BLOCKSIZE:指定以字节为单位的设备缓存的大小,默认值为一4字节整数。
6) CARRIAGECONTROL:指明处理文件中的第一个字符的方式,其值为字符串表达式:
Fortran 对第一个字符作一般的Fortran解释
LIST 指出在文件的每两个记录之间有—个空格
默认状态下,对于连接到打印机和显示器这样的设备,设置值为Fortran,对于连接到文件的设备,设置值为LIST。当FORM被设成UNFORMATTED和BINARY时,其值被忽略。
7) DELIM:指明分隔直接列表或格式化名称列表记录的方式,其值为字符串表达式:
APOSTROPHE 用单撇号(’)分隔
QUOTE 用双撇号(”)分隔
NONE 不用分隔符
如果在OPEN语句中设置了分隔符,则在文件中的单撇号和双撇号都是成对出现的。
8) ERR:出错处理说明。其值是同一程序中的一条语句的标号,当OPEN语句执行出错时执行此语句。如果省略该项,则出错时给出出错信息并终止运行。
9) FILE:文件名。file是一字符串表达式,可以是空、合法的数据文件名字、设备名字或是作为内部文件的变量。在WinNT/9x中允许使用长度大于8的文件名和长度大于3的文件扩展名。省略此项时,编译器将自动产生一个文件名唯一的临时文件,这个临时文件将在结束运行或与文件连接的设备关闭后被删除掉。
10) FORM:记录格式说明。form是字符串表达式:
FORMATTED 记录按有格式存放。
UNFORMATTED 记录按无格式存放。
当省略此说明项时为:对顺序文件是有格式的;对直接文件是无格式的。
11) IOFUS:指出一个新Quickwin子窗口是否为活动窗口,其值为逻辑值。缺省值为真。
12) IOSTAT:出错状态说明。iostat是—个缺省长度为4的整形变量。当执行此OPEN语句时系统给变量赋值:
零 没有发生错误
负数 文件结尾
正数 发生错误,其值视具体计算机系统而定
若省略该项则没有此功能。
13) PAD:从格式化文件中记录的数据少于要读取的数据时,是否用空格来填充没有从记录中读到数据的变量。pad是字符串表达式:
YES 填充(默认值)
NO 不填充
14) POSITION:指定打开顺序文件的访问位置,position是字符串表达式:
ASIA 已被连接的文件的访问位置是固定的,未被连接的文件的访问位置是文件的开始处。
REWIND 把文件的访问位置定在文件的开始处(文件己存在)。
APPEND 把文件的访问位置定在文件的末尾处(文件己存在)。
对于一个新文件,文件的访问位置总是被定在文件的开始处。
15) RECL:记录长度(单位为字节)说明。recl是指定的正整型量或算术表达式,用来指定直接文件中的每条记录的字节数,或顺序文件中的记录的最大长度。
16) SHARE:指明当文件打开时是否实现文件的锁定。share是字符串表达式:
DENYRW 动态读写模式。不允许其他的进程打开这个文件。
DENYWR 动态写模式。不允许其他的进程以写的方式打开这个文件。
DENYRD 动态读模式。不允许其他的进程以读的方式打开这个文件。
DENYNONE 默认的非动态模式。允许其他的进程打开这个文件。
17) STATUS:文件状态说明。status是字符串表达式:
OLD 表示指定的文件是已经存在的老文件。这一状态一般用于读操作,如果用于写操作则重写文件,原文件内容将被覆盖。如果指定的文件并不存在,则系统将给出出错信息。
NEW 表示指定的文件尚不存在。执行OPEN语句时将在磁盘上建立该文件并使其状态改变为OLD。NEW状态一般用于写操作。如果指定的文件名已经存在将给出出错信息(有的系统不给出信息而是把这个已经存在的文件冲掉使原来的内容不复存在)。
SCRATCH 表示与设备号相连接的文件在关闭时将被自动删除。注意:此状态不能与FILE说明共存,只能用于由计算机系统指定的文件名,使该文件作为程序运行过程中的一个临时性文件。
REPLACE 表示替换一个有相同名字的文件,如果没有同名的文件存在,将产生一个新文件。
UNKNOWN 表示文件可以是已存在的或不存在的。系统打开文件状态的次序为:OLO->NEW->创建新文件。STATUS的设置值只影响磁盘文件,像键盘和显示器这样的设备将忽略这一设置。
若省略该项时默认的状态为UNKNOWN。
b) ENDFILE语句
ENDFILE语句的功能是在文件上写一条文件结束记录,这时文件定位在结束记录的后面。它的一般形式为:
ENDFILE{unit|([UNIT=]unit[,ERR=err][,IOSTAT=iostat])}
由于用ENDFILE语句在文件中写入一条结束记录后,文件的指针被定位在结束记录之后,所以若再想向同一个文件中添加更多的记录,就必须使用BACKSPACE或REWIND语句对文件进行文件指针定位的操作。在直接访问文件中使用ENDFILE语句在文件中写入一条结束记录后,新的结束记录后的所有老的记录都将被删除掉。
c) CLOSE语句
CLOSE语句解除设备号与文件的连接,又称关闭文件。它的一般形式为:
CLOSE([UNIT=]unit[,ERR=err][,IOSTAT=iostat][,STATUS|DISPOSE|DISP=status])
其中除STATUS以外的各项参数的意义及取值与OPEN语句中的相同。STATUS是文件关闭后状态说明,其值是一字符串:
DELETE 与设备连接的文件不保留,被删除
KEEP(或SAVE) 与设备号连接的文件保留下来不被删除
PRINT 将文件递交给打印机打印并被保留(仅对顺序文件)
PRINT/DELETE 将文件递交给打印机后被删除
SUBMIT 插入一个进程以执行文件
SUBMIT/DELETE 插入一个进程以执行文件,当插入完成后被删除
默认设置将删除带有SCRATCH属性的临时文件,对其它文件为KEEP。
在程序中,没有必要显示的进行文件的关闭,—般情况下,当程序退出时将以各个文件的默认状态关闭所有的文件。CLOSE语句不必与OPEN语句出现存同一程序单元中。
d) 文件指针定位语句
REWIND语句:称为反绕语句,它使指定设备号的文件指针指向文件的开头,通常用于顺序文件的操作。它的一般形式为:
REWIND{unit|([UNIT=]unit[,ERR=err][,IOSTAT=iostat])
BACKSPACE语句:称为回退语句,它使指定设备号的文件指针退回一个记录位置,一般用于顺序文件。它的一般形式为:
BACKSPACE{unit|([UNIT=]unit[,ERR=err][,IOSTAT=iostat])
除了以下几种情况外,使用BACKSPACE语句正好使文件的指针向前移动一条记录:本条记录前再没有记录时文件指针的位置不变;文件指针的位置在一条记录的中间时,文件指针移到本条记录的开始处;本记录的前—记录是文件结束记录时,文件指针移到文件结束记录之前。
7.2.5 使用硬件设备
在Fortran中标准的输入设备是键盘,标准的输出设备是显示器(控制台)。—般的输入输出语句都是针对标准设备进行操作的,如果想对除键盘和显示器以外的其他的物理设备进行读写操作,就应该把物理设备名描述为文件名,这样就可以像操作文件一样对其进行操作,绝大多数设备名没有扩展名。以下是WinNT/9x下的一些设备名。
设备 | 描述 |
CON | 控制台(即屏幕,标准输出设备) |
PRN | 打印机 |
COMl | 1#串行通信口 |
COM2 | 2#串行通信口 |
COM3 | 3#串行通信口 |
COM4 | 4#串行通信口 |
LPTl | 1#并行通信口 |
LPT2 | 2#并行通信口 |
LPT3 | 3#并行通信口 |
LPT4 | 4#并行通信口 |
NUL | 空(NLTLL)设备。放弃输出,不包含任何输入 |
AUX | 1#串行通信口 |
LINE1 | 1#串行通信口 |
USER1 | 标准输出 |
ERRl | 标准错误 |
CONOUT$ | 标准输出 |
CONIN$ | 标准输入 |
如果使用了这些名字的扩展名,例如LPT1.TXT,Fortran将会写入一个文件而不是相应的设备。下面是打开物理设备作为单元的例子:
例:OPEN(UNIT=4,FILE=’PRN’)
例:OPEN(UNIT=7,FILE=’COM2’,ERR=100)