简体   繁体   English

在FORTRAN中读取输入文件

[英]Reading input files in FORTRAN

Purpose: Create a program that takes two separate files, opens and reads them, assigns their contents to arrays, do some math with those arrays, create a new array with product numbers, print to a new file. 目的:创建一个程序,它接受两个单独的文件,打开并读取它们,将它们的内容分配给数组,对这些数组进行一些数学运算,创建一个带有产品编号的新数组,打印到一个新文件。 Simple enough right? 够简单吧?

My input files have comment characters at the beginning. 我的输入文件在开头有注释字符。 One trouble is, they are '#' which are comment characters for most plotting programs, but not FORTRAN. 一个问题是,它们是'#',它们是大多数绘图程序的注释字符,但不是FORTRAN。 What is a simple way to tell the computer not to look at these characters? 什么是告诉计算机不要查看这些字符的简单方法? Since I have no previous FORTRAN experience, I am plowing through this with two test files. 由于我之前没有FORTRAN经验,我正在通过两个测试文件来完成这项工作。 Here is what I have so far: 这是我到目前为止:

PROGRAM gain
  IMPLICIT NONE
  REAL, DIMENSION (1:4, 1:8)     :: X, Y, Z
  OPEN(1, FILE='test.out', &
        STATUS='OLD', ACTION='READ')            ! opens the first file
  READ(1,*), X
  OPEN(2, FILE='test2.out', &
    STATUS='OLD', ACTION='READ')            ! opens the second file
  READ(2,*), Y
  PRINT*, X, Y

  Z = X*Y
!  PRINT*, Z
  OPEN(3, FILE='test3.out', STATUS='NEW', ACTION='WRITE')   !creates a new file
  WRITE(3,*), Z
  CLOSE(1)
  CLOSE(2)
  CLOSE(3)
END PROGRAM

PS. PS。 Please do not overwhelm me with a bunch of code monkey gobblety gook. 请不要用一堆代码猴子gobblety gook压倒我。 I am a total programming novice. 我是一个总编程新手。 I do not understand all the lingo, that is why I came here instead of searching for help in existing websites. 我不懂所有的术语,这就是为什么我来到这里而不是在现有网站上寻求帮助的原因。 Thanks. 谢谢。

If you mean that the comments are only at the beginning of the file, it is fairly simple -- no need to count the comment lines or rewind file -- you can read the lines into a string and test whether they are a comment. 如果您的意思是注释仅在文件的开头,那么它非常简单 - 无需计算注释行或倒回文件 - 您可以将这些行读入字符串并测试它们是否是注释。 Then you will eventually encounter a non-comment line. 然后你最终会遇到一个非评论行。 Problem: it will have been read into a string and thus not available for a regular read ... solution ... use "backspace" to unread one record so that you can now use normal file reads to read the rest of the file. 问题:它将被读入一个字符串,因此无法用于常规读取...解决方案...使用“退格”来读取一条记录,以便您现在可以使用普通文件读取来读取文件的其余部分。 A slightly more complicated solution would be necessary if comment lines were interspersed throughout the file -- as already stated, read the lines into a string, then read from the string. 如果注释行穿插在整个文件中,则需要稍微复杂的解决方案 - 如前所述,将行读入字符串,然后从字符串中读取。

Here is a worked example ... I have assumed that the "#" is in the first column and various other simplifying assumptions. 这是一个有效的例子......我假设“#”在第一列和各种其他简化假设中。 Some recommendations: put your subroutines and functions into a module and "use" that module -- this will allow the compiler to check the interfaces. 一些建议:将子程序和函数放入模块并“使用”该模块 - 这将允许编译器检查接口。 As you are developing your programs, use as many code checking and warning options as possible -- especially subscript bounds checking -- it will save time in the end. 在开发程序时,尽可能多地使用代码检查和警告选项 - 特别是下标边界检查 - 最终会节省时间。

PS It is officially "Fortran" since Fortran 90 -- it was "FORTRAN" for FORTRAN 77 and earlier. PS它自Fortran 90以来正式成为“Fortran” - 它是FORTRAN 77及更早版本的“FORTRAN”。

module read_file_module

   implicit none

contains

   subroutine read_file (UnitNum, FileName, NumRows, NumCols, Array )

      integer, intent (in) :: UnitNum
      character (len=*), intent (in) :: FileName
      integer, intent (in) :: NumRows, NumCols
      real, dimension (1:NumRows, 1:NumCols), intent (out) :: Array

      character (len=300) :: line
      integer :: i, j

      open (unit=UnitNum, file=FileName, status='old', action='read' )

      ReadComments: do
         read (UnitNum, '(A)') line
         if (line (1:1) /= "#") exit ReadComments
      end do ReadComments

      backspace (UnitNum)

      do i=1, NumRows
         read (UnitNum, *) (Array (i, j), j=1,NumCols)
      end do

      close (UnitNum)

      return

   end subroutine read_file

end module read_file_module 




program test_prog

use read_file_module

implicit none

real, dimension (1:8, 1:4) :: Array
integer :: i, j

call read_file (66, 'TestFile.txt', 8, 4, Array)

do i=1, 8
  write (*, '( 4(2X, F7.3) )' ) (Array (i, j), j=1,4)
end do

end program test_prog

And some test data to show how flexible the input data can be: 以及一些测试数据显示输入数据的灵活性:

#  comment one
#  comment two
1.1   2.0  3.0  4.1
1.2   2.0  3.0  4.2
1.3   2.0  3.0  4.3
1.4   
  2.0  3.0  4.4
1.5   2.0  3.0  4.5
1.6   2.0  3.0  4.6


1.7   2.0  3.0  4.7
1.8   2.0  3.0  4.8

Write a subroutine that puts this logic into one spot for you so you can call it for both files. 编写一个子程序,将此逻辑放入一个位置,以便您可以为两个文件调用它。 You'll need to read each line as a string and add an IF test to check whether a given line starts with a "#" or not. 您需要将每行读作字符串并添加IF测试以检查给定行是否以“#”开头。 If the line starts with a "#", just read the next line. 如果该行以“#”开头,​​则只需阅读下一行。 If not, convert the string to a value and add it to the array of values you're returning. 如果没有,请将字符串转换为值并将其添加到要返回的值数组中。

I'm not real familiar with anything beyond FORTRAN 77 but here are a few pointers (and a working version of what you posted in your answer). 我对FORTRAN 77以外的任何东西都不熟悉,但这里有一些指针(以及你在答案中发布的工作版本)。 First the working code (I added line numbers): 首先是工作代码(我添加了行号):

1   REAL FUNCTION myfile(unit, file, rows, columns)
2   IMPLICIT NONE
3   INTEGER, INTENT(IN) :: unit, rows, columns
4   CHARACTER(LEN=*) :: file
5   REAL, DIMENSION (1:columns, 1:rows) ::X
6   OPEN(unit, FILE=file, STATUS='OLD', ACTION='READ')
7   READ(unit,*), X
8   PRINT*, X         
9   CLOSE(unit)
10  myfile= 0
11  END FUNCTION myfile
12
13  PROGRAM gain
14  errno = myfile(1, "test.out", 8, 4)
15  END PROGRAM

The differences are: 不同之处是:

  • line 6 - You needed to remove the quotes around 'file' in the FILE='file' assignment. 第6行 - 您需要删除FILE ='file'赋值中'file'周围的引号。 As written you were using a file named file instead of the name passed in as the file parameter. 如上所述,您使用的是名为file的文件而不是作为file参数传入的名称。
  • line 10 - since you declared this as a function you need to assign a return value to the function name before you leave the function. 第10行 - 由于您将此声明为函数,因此在离开函数之前需要为函数名指定返回值。 I just assigned it a value of 0 to allow it to compile. 我只是给它赋值0以允许它编译。 I think what you want here is to pass the array you read in from the file back out of the routine. 我想你在这里想要的是将你从文件中读入的数组传递回例程。 In that case you'll need to modify the type of the function (and assign X to myfile) or pass the array in as a parameter and allow it to be modified in the function. 在这种情况下,您需要修改函数的类型(并将X分配给myfile)或将数组作为参数传递,并允许在函数中修改它。 (in the old FORTRAN 77 world this was done with common blocks or pointers to the array, not sure how you do it in later versions of Fortran). (在旧的FORTRAN 77世界中,这是通过公共块或指向数组的指针完成的,不确定如何在更高版本的Fortran中执行此操作)。
  • line 14 - you need to assign the return value of the function to a variable. 第14行 - 您需要将函数的返回值赋给变量。 At least you did with my gfortran compiler. 至少你用我的gfortran编译器做了。 It wouldn't let me compile the program otherwise. 否则我不会让我编译程序。
  • line 14 (again) - the function call needs the name of the file (test.out) in quotes. 第14行(再次) - 函数调用需要引号中的文件名(test.out)。 You had it without quotes and so was having problems (this might be where your array reference error was coming from, I got different errors with my compiler.) 你有没有引号,所以有问题(这可能是你的数组引用错误来自的地方,我的编译器出现了不同的错误。)

Instead of using a function, you could define this your myfile routine as a subroutine instead. 您可以将myfile例程定义为子例程,而不是使用函数。 In this case you would definitely need to pass in the array you want filled as a parameter. 在这种情况下,您肯定需要传入您想要填充的数组作为参数。 You wouldn't need line 10 and instead of assigning the return value to a variable in the main program you would 'call' the routine. 您不需要第10行,而是将返回值分配给主程序中您将“调用”例程的变量。 ie line 14 would look like this: 即第14行看起来像这样:

call myfile(1, "test.out",8,4)

EDIT: I posted this and then realized I forgot to answer the original questions. 编辑:我发布了这个,然后意识到我忘了回答原来的问题。 I did so and then for some reason couldn't connect to SO to upload the edits. 我这样做然后由于某种原因无法连接到SO上传编辑。 So here they are finally. 所以这里终于来了。

That gets your routine compiling. 这会让你的日常编译。 To actually deal with the comment lines, you have several options (at least these are the ones that come to mind initially). 要真正处理注释行,您有几个选项(至少这些是最初想到的)。 These are in order from simplest/most brittle to more robust/general: 这些是从最简单/最脆弱到更强大/一般的顺序:

  1. If you know exactly how many comment lines are in the data file (and it is the same for all the files) you could just read in that many lines, throw away what was read in and then read in the array from that point. 如果您确切地知道数据文件中有多少注释行(并且对于所有文件都是相同的),您可以读取多行,抛弃读入的内容,然后从该点读取数组。 This will advance you past the comments and then read in the array. 这将使您超越注释,然后读入数组。 However if the number of comment lines is different in the different input files, this will not work. 但是,如果不同输入文件中的注释行数不同,则不起作用。 Likewise if it changes in the future it will require changing the code. 同样,如果将来发生变化,则需要更改代码。 This option is probably not the best. 这个选项可能不是最好的。
  2. Make a pass through the file read a line at a time as a string and checking to see if it starts with a # mark. 将文件传递作为字符串一次读取一行,并检查它是否以#标记开头。 If so increment a counter. 如果这样增加一个计数器。 When you find the first non-comment line, stop, reset the file to the beginning and then follow the steps in #1 above where you use the counter value as the number of lines to skip. 当您找到第一个非注释行时,停止,将文件重置为开头,然后按照上面#1中的步骤使用计数器值作为要跳过的行数。 This is more flexible than #1 in that it can handle an arbitrary number of comment lines but they still have to be at the beginning of the file. 这比#1更灵活,因为它可以处理任意数量的注释行,但它们仍然必须位于文件的开头。 Any comments in the middle of the data will mess you up. 数据中间的任何评论都会让你感到困惑。
  3. Read in each line as a string, look for the # symbol and if it is not there, parse the line and populate the array manually. 以字符串形式读入每一行,查找#符号,如果不存在,则解析该行并手动填充该数组。 This is the most complex but gives the most flexibility for the input file format. 这是最复杂的,但为输入文件格式提供了最大的灵活性。 It allows you to have (and ignore) comments anywhere in the data file. 它允许您在数据文件中的任何位置拥有(并忽略)注释。

Which method you choose (and other people may have other suggestions) depends on your particular application. 您选择哪种方法(以及其他人可能有其他建议)取决于您的特定应用。 Good luck. 祝好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM