第7章 输入&输出
(7.1) 格式化WRITE语句
表控输出(List-directed input):输出语句的输出列表中的类型决定输出数据的格式。
WRITE(*,*) "Hello,Fortran!"
!WRITE(unit=*,fmt=*) "Hello,Fortran!"
PRINT*,"Hello,Fortran!"
(1)第一个星号代表默认设备即屏幕;第二个星号代表不设置输出格式。
(2)WRITE
的区别是PRINT没有设置输出位置的能力,只能针对屏幕输出。
(3)WRITE
语句执行后会自动换行。
这里不讨论输出设备。在设置格式时有以下三种风格:
!第一种写法
WRITE(*,"('a = ',I3)") a
!第二种写法
WRITE(*,100) a
100 FORMAT("a = ",I3)
!第三种写法
CHARACTER(10) :: str = "('a = ',I3)"
WRITE(*,str) a
整型输出:[n]Iw[.m](输出n个整型数,每个整型数占w个字符宽度,其中纯数字部分占m个字符宽度)
WRITE(*,"(I5)") -12 !□□-12
WRITE(*,"(I5.3)") -12 !□-012
WRITE(*,"(I3)") 1000 !***
WRITE(*,"(I0)") -12 !-12
实型输出:[n]Fw.d(输出n个实型数,每个实型数占w个字符宽度,其中小数部分占d个字符宽度)
WRITE(*,"(F9.3)") 123.45 !□□123.450
非标准科学计数法输出:[n]Ew.d[Ee](以非标准科学计数法的方式输出n个实型数,每个实型数占w个字符宽度,尾数在0.1~1.0之间,尾数的小数部分占d个字符宽度,指数部分占e个字符宽,一般要求w ≥ \ge ≥d+7)
WRITE(*,"(ES15.7)") 123.45 !□□1.2345000E+02
WRITE(*,"(ES9.2E3)") 12.34 !1.23E+001
标准科学计数法输出:[n]ESw.d[Ee](以标准科学计数法的方式输出n个实型数,每个实型数占w个字符宽度,尾数在1.0~10.0之间,尾数的小数部分占d个字符宽度,指数部分占e个字符宽,一般要求w ≥ \ge ≥d+7)
WRITE(*,"(E15.7)") 123.45 !□□0.1234500E+03
WRITE(*,"(E9.2E3)") 12.34 !0.12E+002
逻辑型输出:[n]Lw(输出n个逻辑型数据,每个逻辑型数据占w个字符宽度)
WRITE(*,"(L3)") .TRUE. !□□T
字符(串)型输出:[n]A[w](输出n个字符(串)型数据,每个字符(串)型数据占w个字符宽度)
WRITE(*,"(A6)") "Hello" !□Hello
WRITE(*,"(A3)") "Hello" !Hel
WRITE(*,"(A)") "Hello" !Hello
WRITE(*,"(2A6)") "Hello","World" !□Hello□World
左右移动位置输出:nX、TRn、TLn
WRITE(*,"(2X,I3)") 100 !先向右移动2个单位,再输出100,即:□□100
WRITE(*,"(TR2,I3)") 100 !先向右移动2个单位,再输出100,即:□□100
WRITE(*,"(A10,TL3,I3)") "Call 119",110 !□□Call□110
跳跃至特定列输出:Tc
WRITE(*,"(T3,I3)") 100 !□□100
换行输出:/
WRITE(*,"(I3/I3)") 10,10
WRITE(*,"(I3,/,I3)") 10,10 !两种写法等价
下面介绍几种重复使用某一个或某一组格式的方法:
100 FORMAT(I2,2(I3,F3.1)) !100 FORMAT(I2,I3,F3.1,I3,F3.1)
100 FORMAT(I2,*(I3,F3.1)) !无限使用(I3,F3.1)描述符,直到没有可输出的数据为止
(7.2) 格式化READ语句
表控输入(List-directed input):变量列表中的变量类型决定输入数据需要的格式。
INTEGER :: i
READ(*,*) i
!READ(unit=*,fmt=*) i
(1)若要同时输入多个数据,可以用逗号或空格或回车将数据分开。
(2)第一个星号代表默认设备即键盘;第二个星号代表不设置输入格式。
(3)在读入字符串时,如果字符串中含有空格或逗号,则需要将字符串用单/双引号括起来。
(4)如果读取的字符串超出设定范围,多余部分被舍去;小于设定范围,多余部分以空格填充。
(5)在读取含有逗号或空格的字符串如Hello,□world
时,必须将读取格式设置为A12
。
这里不讨论输入设备。在设置格式时有以下三种风格,这三种写法的意思均为指定跳过输入缓冲区的前6列,然后第7~12列的内容将被解释为整数,并将输入的数据保存到整型变量a中。
!第一种写法
READ(*,"(6X,I6)") a
!第二种写法
READ(*,100) a
100 FORMAT(6X,I6)
!第三种写法
CHARACTER(10) :: str = "(6X,I6)"
READ(*,str) a
(7.3) 文件操作
打开文件
OPEN(KEYWORD = xxx,...)
- UNIT:设置文件代码,可以是常量或变量,但应该避开键盘的位置(1、5)和屏幕的位置(2、6)。
- FILE:设置所要操作文件的名称,Windows不区分大小写,Unix区分大小写。
- FORM:设置文件的保存格式。‘formated’(默认)表示文件使用“文本文件”格式保存;'unformatted’表示文件使用“二进制”格式保存。
- STATUS:设置文件状态。'new’表示文件不存在;'old’表示文件原本就已经存在;'replace’表示若文件存在重新创建,若不存在就新建;'scratch’表示打开暂存盘,程序结束后就自动删除;‘unknown’(默认)通常和’replace’一样。
- ACCESS:设置读取文件的方法。‘sequential’(默认)表示顺序读取;'direct’表示直接读取。
- RECL:设置一次可以读写多大容量的数据(顺序读取);设置文件中每一个模块单元的分区长度(直接读取)。
- ERR:设置当文件打开发生错误时,程序跳跃到行代码xxx处继续执行程序。
- IOSTAT:获取文件的打开状态。文件终了xxx<0;读取正常xxx=0;读取发生错误xxx>0。
- BLANK:设置字段中空格所代表的意义。 'null’表示空格代表没有东西;'zero’表示空格代表0。
- POSITION:设置文件打开时的读写位置。 'asis’表示文件打开时的读取位置,通常是文件的开头;'rewind’表示文件打开时的读取位置移动到文件开头;'append’表示文件打开时的读取位置移动到文件结尾。
- ACTION:设置待打开文件的读写权限。'read’表示只读;'write’表示只写;‘readwrite’(默认)表示读写。
- PAD:设置格式化输入时设置最前面的不足字段是否以空格填满。取值为’yes’(默认)、‘no’。
- DELIM:设置输出的字符串的显示风格。'none’表示纯粹输出字符串的内容;'quote’表示给输出字符串加上双引号;'apostrophe’表示给输出字符串加上单引号。
关闭文件
CLOSE(KEYWORD = xxx,...)
- UNIT:设置文件代码,可以是常量或变量,但应该避开键盘的位置(1、5)和屏幕的位置(2、6)。
- STATUS:设置文件关闭后文件的去留。‘keep’(默认)表示保留文件;'delete’表示删除文件。
- ERR:设置当文件关闭发生错误时,程序跳跃到行代码xxx处继续执行程序。
- IOSTAT:获取文件的关闭状态。文件终了xxx<0;关闭正常xxx=0;关闭发生错误xxx>0。
写数据
WRITE(KEYWORD = xxx,...)
- UNIT:设置文件代码,可以是常量或变量,但应该避开键盘的位置(1、5)和屏幕的位置(2、6)。
- FMT:设置写入格式。
- REC:设置所要写的文件的模块位置(直接读取)。
- ERR:设置当文件写入发生错误时,程序跳跃到行代码xxx处继续执行程序。
- IOSTAT:获取文件的写入状态。文件终了xxx<0;写入正常xxx=0;写入发生错误xxx>0。
- END:设置当写到文件末尾时,程序跳跃到行代码xxx处继续执行程序。
- NML:详细用法见namelist小节。
- ADVANCE:设置在文本格式下的顺序文件中,每一次写入后,写入位置是否自动换行,取值为’yes’、‘no’。
- SIZE:当ADVANCE='no’时,获取写入的字符数目。
读数据
READ(KEYWORD = xxx,...)
- UNIT:设置文件代码,可以是常量或变量,但应该避开键盘的位置(1、5)和屏幕的位置(2、6)。
- FMT:设置读取格式。
- REC:设置所要读取文件的模块位置(直接读取)。
- ERR:设置当文件读取发生错误时,程序跳跃到行代码xxx处继续执行程序。
- IOSTAT:获取文件的读取状态。文件终了xxx<0;读取正常xxx=0;读取发生错误xxx>0。
- END:设置当读到文件末尾时,程序跳跃到行代码xxx处继续执行程序。
- NML:详细用法见namelist小节。
- ADVANCE:设置在文本格式下的顺序文件中,每一次读取后,读取位置是否自动换行,取值为’yes’、‘no’。
- SIZE:当advance='no’时,获取读取的字符数目。
查询文件状态
INQUIRE(KEYWORD = xxx,...)
- UNIT:设置文件代码,可以是常量或变量,但应该避开键盘的位置(1、5)和屏幕的位置(2、6)。
- FILE:设置所要操作文件的名称,Windows不区分大小写,Unix区分大小写。
- IOSTAT:获取文件读取情况。文件终了xxx<0;读取正常xxx=0;读取发生错误xxx>0。
- ERR:设置当文件打开发生错误时,程序会跳跃到value所指的行代码处来继续执行程序。
- EXIST:检查文件是否存在,返回一个布尔变量给xxx,存在为.true.,反之为.false.。
- OPENED:检查文件是否已打开,返回一个布尔变量给xxx,已打开为.true.,反之为.false.。
- NUMBER:获取文件代码。
- NAMED:检查文件是否取了名字,也就是检查文件是否为临时保存盘。
- ACCESS:获取文件的读取方式,返回一个字符串。'sequential’表示文件是顺序读取;'direct’表示文件是直接读取;'undefined’表示没有定义。
- SEQUENTIAL:检查文件是否使用顺序读取,返回字符串’yes’、‘no’、‘unknown’。
- DIRECT:检查文件是否使用直接读取,返回字符串’yes’、‘no’、‘unknown’。
- FORM:获取文件保存方法,返回一个字符串。'formatted’表示文本文件;'unformatted’表示二进制文件;'unknown’表示没有定义。
- FORMATTED:检查文件是否为文本文件,返回字符串’yes’、‘no’、‘unknown’。
- UNFORMATTED:检查文件是否为二进制文件,返回字符串’yes’、‘no’、‘unknown’。
- RECL:获取打开文件时RECL栏的设定值。
- NEXTREC:获取下一次文件读写的位置。
- BLANK:获取打开文件时BLANK栏的设定值。
- POSITION:获取打开文件时POSITION栏的设定值。
- ACTION:获取打开文件时ACTION栏的设定值。
- READ:检查文件是否为只读文件,返回字符串’yes’、‘no’、‘unknown’。
- WRITE:检查文件是否为只写文件,返回字符串’yes’、‘no’、‘unknown’。
- READWRITE:检查文件是否为读写文件,返回字符串’yes’、‘no’、‘unknown’。
- DELIM:获取打开文件时DELIM栏的设定值。
- PAD:获取打开文件时PAD栏的设定值。
文件读写位置移动
BACKSPACE(UNIT = xxx,ERR = xxx,IOSTAT = xxx) !把文件的读写位置退回一步,其他字段参考前面
ENDFILE(UNIT = xxx,ERR = xxx,IOSTAT = xxx) !把目前文件的读写位置变成文件的结尾,其他字段参考前面
REWIND(UNIT = xxx,ERR = xxx,IOSTAT = xxx) !把文件的读写位置倒回文件开头,其他字段参考前面
顺序文件
顺序文件在读写时,不能任意跳跃到文件的某个位置读写数据,只能从头开始一步步向下进行。改变文件读写位置时,只能一步步地进退,或是直接移回文件开头或结尾。顺序文件的读取操作叫做顺序读取。来看两个写入文件、读取文件例子:
!学生成绩录入程序
PROGRAM main
IMPLICIT NONE
TYPE student
CHARACTER(LEN = 10) :: name
INTEGER :: chinese,math
END TYPE student
CHARACTER(LEN = 20) :: file_name = "e:\score.txt"
INTEGER,PARAMETER :: file_id = 10
INTEGER :: i,num,allocate_stat = 0
TYPE(student),ALLOCATABLE :: students(:)
WRITE(*,*) "请输入学生数量:"
READ(*,*) num
ALLOCATE(students(num),STAT = allocate_stat)
IF(allocate_stat /= 0) THEN
WRITE(*,*) "分配空间失败!"
STOP
END IF
OPEN(UNIT = file_id,FILE = file_name,STATUS = 'replace')
DO i = 1,num
WRITE(*,"('请输入第',I3,'个学生的姓名:')") i
READ(*,"(A8)") students(i)%name
WRITE(*,*) "请输入语文、数学成绩:"
READ(*,"(I3,I3)") students(i)%chinese,students(i)%math
WRITE(UNIT = file_id,FMT = "(A8,I3,I3)") students(i)%name,students(i)%chinese,students(i)%math
END DO
CLOSE(UNIT = file_id)
END PROGRAM main
!显示目标文件内容程序
PROGRAM main
IMPLICIT NONE
INTEGER,PARAMETER :: file_id = 10
CHARACTER(LEN = 79) :: file_name
!这里之所以限制读取字符串的长度为79是因为在标准的DOS及Windows的cmd窗口下
!一行只能显示80个字符,所以读太多字符在cmd窗口就显示不出来了,但是如果刚好输出80个字符
!有的编译器所编译出来的程序会发生断行的现象,所以最好是79
CHARACTER(LEN = 79) :: buffer !缓冲器,用来接收文件数据
INTEGER :: read_stat = 0 !记录文件的读取状态
LOGICAL :: is_exist !判断文件是否存在
WRITE(*,*) "请输入文件名:"
READ(*,"(A79)") file_name
INQUIRE(FILE = file_name,EXIST = is_exist)
IF(.NOT.is_exist) THEN
WRITE(*,*) TRIM(file_name),"文件不存在!"
STOP
END IF
OPEN(UNIT = file_id,FILE = file_name,ACCESS = 'sequential',STATUS = 'old') !顺序读取、文件已存在
DO WHILE(.TRUE.)
READ(UNIT = file_id,FMT = "(A79)",IOSTAT = read_stat) buffer !最多读79个字符
IF(read_stat /= 0) EXIT
WRITE(*,"(A79)") buffer !每读入一行后,就把这一行的文本写到屏幕上,然后READ自动换行继续读下一行
END DO
CLOSE(file_id)
END PROGRAM main
!用如下格式写入文件
WRITE(UNIT = file_id,FMT = "('座位号:',I2,/,'成绩:',I2)") i,score
!如果只想要读取i和score,那么与之严格对应的读取格式为:
READ(*,"(8X,I2,/,6X,I2)")
(1)文件代码通常定义成常量,在编写大型程序时,有助于修改以及分清文件。
(2)如果省略关闭文件的操作,程序结束时会自动关闭,不过还是养成手动关闭文件的习惯。
(3)文件的id在整个程序中是共享的,不同的函数可以使用同样的id来读写同一个文本文件。
(4)操作系统通常会限制一个程序所能够同时打开的文件数目,最好还是不要同时打开很多个文件。
直接访问文件
直接访问文件把文件的空间、内容事先分区成好几个同样大小的小模块,这些模块会自动编号。读写文件时要先赋值文件读写位置在第几个模块,再来进行读写操作,所以直接访问文件可以到文件的任意地方进行读写。直接访问文件的读取操作叫做直接读取。
假如现在有4个学生的成绩如下,如何快速地取出第三位学生的成绩呢?这便是直接读取所要解决的问题。
PROGRAM main
IMPLICIT NONE
INTEGER,PARAMETER :: file_id = 10
CHARACTER(LEN = 79) :: file_name = "e:\score.txt"
INTEGER :: student
REAL :: score
!RECL=6表示设置每个模块的长度
!至于为什么是6,是因为在这个文件中的每一行恰好都有4个字符
!但是在Microsoft操作系统中,文本文件每一行的行尾都有2个看不见的符号用来代表一行文本的结束
!(可以用来将相邻的数据分开,不至于让人看起来是一长串数字)
!所以每行文本的长度要加2,因此是6(如果是UNIX系统,则加的是1)
OPEN(UNIT = file_id,FILE = file_name,ACCESS = 'direct',FORM = 'formatted',RECL = 6)
!打开直接读取文件时,OPEN命令中的ACCESS = 'direct'及RECL = 6都不能省略
!ACCESS='direct'时,FORM的默认值是'unformatted',所以要记得加上FORM='formatted'
WRITE(*,*) "您想要查询第几位学生的成绩:"
READ(*,*) student
!REC=student表示读取第student个模块
READ(UNIT = file_id,FMT = "(F4.1)",REC = student) score
WRITE(*,"('该学生的成绩为:',F4.1)") score
CLOSE(file_id)
END PROGRAM main
内部文件
内部文件(Internal File)也叫做字符串变量文件。
PROGRAM main
IMPLICIT NONE
INTEGER :: a = 2,b = 3,c;
CHARACTER(20) :: str1,str2 = '123'
WRITE(str1,FMT = "(I2,'+',I2,'=',I2)") a,b,a+b
WRITE(*,"(A20)") str1 !□2+□3=□5
READ(str2,*) c
WRITE(*,*) c !□□□□□□□□□123
END PROGRAM main
内部文件的作用一:在使用READ命令从键盘输入数据时,如果用户输入错误的数据,会导致程序死机(例如如果需要输入整数时却输入英文字母,就可能会死机)。比较好的处理方法是程序暂时把数据当成字符串读入,检查字符串中是否含有不合理的字符,如果字符串中都是0~9的数字,就把字符串转成整数,不然就让用户重新输入。
INTEGER FUNCTION integer_input_only()
IMPLICIT NONE
CHARACTER(LEN = 10) :: str
INTEGER :: i
LOGICAL :: flag = .TRUE.
DO WHILE(flag)
WRITE(*,*) "请输入正整数:"
READ(*,"(A10)") str
flag = .FALSE.
DO i = 1,LEN_TRIM(str)
IF(ICHAR(str(i:i)) < ICHAR('0') .OR. ICHAR(str(i:i)) > ICHAR('9')) THEN
WRITE(*,*) "输入错误,请重新输入!"
flag = .TRUE.
EXIT
END IF
END DO
END DO
READ(str,*) integer_input_only !以整型返回
RETURN
END FUNCTION integer_input_only
PROGRAM main
IMPLICIT NONE
INTEGER,EXTERNAL :: integer_input_only
WRITE(*,*) integer_input_only()
END PROGRAM main
内部文件的作用二:可以动态改变输出格式。输出格式可以事先放在字符串中,程序运行时,动态改变字符串的内容就可以动态改变输出格式。
PROGRAM main
IMPLICIT NONE
INTEGER :: a = 1,b = 2
CHARACTER(LEN = 30) :: fmt_str = "(I??,'+',I??,'=',I??)"
WRITE(fmt_str(3:4),"(I2.2)") INT(LOG10(REAL(a))+1)
WRITE(fmt_str(11:12),"(I2.2)") INT(LOG10(REAL(b))+1)
WRITE(fmt_str(19:20),"(I2.2)") INT(LOG10(REAL(a+b))+1)
WRITE(*,fmt_str) a,b,a+b !1+2=3
END PROGRAM main
NAMELIST
NAMELIST可以把一组相关的变量封装在一起,输入/输出这一组变量时,只要在WRITE/READ中的NML字段赋值要使用哪一个NAMELIST就可以了。NAMELIST算是声明的一部分,要写在程序执行命令之前。输出NAMELIST的时候不能设置输出格式。
PROGRAM main
IMPLICIT NONE
INTEGER :: a = 1,b = 2,c = 3
NAMELIST /name_list/ a,b,c
WRITE(*,NML = name_list)
END PROGRAM main
!&name_list
!A□□□□□□□=□□□□□□□□□□□1,
!B□□□□□□□=□□□□□□□□□□□2,
!C□□□□□□□=□□□□□□□□□□□3
!/
NAMELIST也可以用来输入数据,不过通常用来读取文件,很少用来键盘读入数据。在接下来这个例子中,用户不能随便输入3个数字了事,必须按照上面介绍的格式来输入。变量可以不按照顺序输入,程序会自动按照变量名来设置数值,变量甚至可以重复输入,不过变量会得到最后一次设置的值。
PROGRAM main
IMPLICIT NONE
INTEGER :: a,b,c
NAMELIST /name_list/ a,b,c
READ(*,NML = name_list)
WRITE(*,NML = name_list)
END PROGRAM main
!&name_list a=1 b=2 c=3 /
!
!&name_list
!A□□□□□□□=□□□□□□□□□□□1,
!B□□□□□□□=□□□□□□□□□□□2,
!C□□□□□□□=□□□□□□□□□□□3
!/
《 F O R T R A N S Y N T A X 》 系 列 博 客 创 作 参 考 资 料 来 源 《FORTRAN\ SYNTAX》系列博客创作参考资料来源 《FORTRAN SYNTAX》系列博客创作参考资料来源
- 《Fortran95程序设计》.彭国伦.中国电力出版社.
- 《工程分析程序设计》.陈斌、周屈兰.西安交通大学出版社.
- 《Fortran程序设计(第四版)》.Stephen J.Chapman.中国电力出版社.
博 客 创 作 : A i d e n L e e 博客创作:Aiden\ Lee 博客创作:Aiden Lee
特别声明:文章仅供学习参考,转载请注明出处,严禁盗用!