在 FORTRAN 中读取输入文件

发布于 2024-09-01 08:23:57 字数 819 浏览 6 评论 0原文

目的:创建一个程序,获取两个单独的文件,打开并读取它们,将它们的内容分配给数组,对这些数组进行一些数学运算,创建一个包含产品编号的新数组,打印到新文件。够简单吧?

我的输入文件开头有注释字符。一个麻烦是,它们是“#”,这是大多数绘图程序的注释字符,但 FORTRAN 不是。告诉计算机不要看这些字符的简单方法是什么?由于我以前没有 FORTRAN 经验,因此我正在使用两个测试文件来研究这个问题。这是我到目前为止所得到的:

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。请不要用一堆代码让我不知所措。我是一个完全的编程新手。我不明白所有的行话,这就是为什么我来到这里而不是在现有网站中寻找帮助。谢谢。

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. 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. 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. Please do not overwhelm me with a bunch of code monkey 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

手心的温暖 2024-09-08 08:23:57

如果您的意思是注释仅位于文件的开头,则相当简单 - 无需计算注释行数或倒带文件 - 您可以将这些行读入字符串并测试它们是否是注释。然后你最终会遇到非注释行。问题:它将被读入字符串,因此无法用于常规读取...解决方案...使用“退格键”取消读取一条记录,以便您现在可以使用正常的文件读取来读取文件的其余部分。如果注释行散布在整个文件中,则需要稍微复杂的解决方案 - 如前所述,将这些行读入字符串,然后从字符串中读取。

这是一个有效的例子......我假设“#”位于第一列和各种其他简化假设。一些建议:将子例程和函数放入模块中并“使用”该模块——这将允许编译器检查接口。当您开发程序时,请使用尽可能多的代码检查和警告选项 - 特别是下标边界检查 - 它最终会节省时间。

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

一些测试数据显示了输入数据的灵活性:

#  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

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.

P.S. It is officially "Fortran" since Fortran 90 -- it was "FORTRAN" for FORTRAN 77 and earlier.

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
牛↙奶布丁 2024-09-08 08:23:57

编写一个子例程,将该逻辑放入一个位置,以便您可以为两个文件调用它。您需要将每一行作为字符串读取,并添加 IF 测试来检查给定行是否以“#”开头。如果该行以“#”开头,则只需阅读下一行。如果不是,请将字符串转换为值并将其添加到您要返回的值数组中。

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 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.

饭团 2024-09-08 08:23:57

我对 FORTRAN 77 之外的任何内容都不太熟悉,但这里有一些提示(以及您在答案中发布的内容的工作版本)。首先是工作代码(我添加了行号):

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

差异是:

  • 第 6 行 - 您需要删除 FILE='file' 赋值中“file”周围的引号。正如所写,您使用的是名为 file 的文件,而不是作为文件参数传入的名称。
  • 第 10 行 - 由于您将其声明为函数,因此您需要在离开函数之前为函数名称分配一个返回值。我只是给它赋了一个值 0 以允许它编译。我认为您想要的是将您从文件中读入的数组传递回例程之外。在这种情况下,您需要修改函数的类型(并将 X 分配给 myfile)或将数组作为参数传递并允许在函数中对其进行修改。 (在旧的 FORTRAN 77 世界中,这是通过公共块或指向数组的指针完成的,不确定在更高版本的 Fortran 中如何做到这一点)。
  • 第 14 行 - 您需要将函数的返回值分配给变量。至少你用我的 gfortran 编译器做到了。否则它不会让我编译程序。
  • 第 14 行(再次)- 函数调用需要用引号引起来的文件名 (test.out)。您没有使用引号,因此遇到了问题(这可能是您的数组引用错误的来源,我的编译器遇到了不同的错误。)

您可以将 myfile 例程定义为子例程,而不是使用函数。在这种情况下,您肯定需要传入要填充的数组作为参数。您不需要第 10 行,并且无需将返回值分配给主程序中的变量,而是“调用”例程。即第 14 行看起来像这样:

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

编辑:
我发布了此内容,然后意识到我忘记回答最初的问题。我这样做了,然后由于某种原因无法连接到 SO 来上传编辑。所以他们终于来了。

这样就可以进行例行编译了。要实际处理注释行,您有多种选择(至少这些是最初想到的)。这些按从最简单/最脆弱到更健壮/一般的顺序排列:

  1. 如果您确切知道数据文件中有多少注释行(并且对于所有文件都是相同的),您可以只读取那么多行,然后扔掉读入的内容然后从该点读入数组。这将使您跳过注释,然后读取数组。但是,如果不同输入文件中的注释行数不同,则这将不起作用。同样,如果将来发生变化,也需要更改代码。这个选项可能不是最好的。
  2. 遍历文件,一次读取一行作为字符串,并检查它是否以 # 标记开头。如果是这样,则增加一个计数器。当您找到第一个非注释行时,停止,将文件重置到开头,然后按照上面 #1 中的步骤操作,其中使用计数器值作为要跳过的行数。这比 #1 更灵活,因为它可以处理任意数量的注释行,但它们仍然必须位于文件的开头。数据中间的任何注释都会让你感到困惑。
  3. 将每一行作为字符串读取,查找 # 符号,如果不存在,则解析该行并手动填充数组。这是最复杂的,但为输入文件格式提供了最大的灵活性。它允许您在数据文件中的任何位置添加(并忽略)注释。

您选择哪种方法(其他人可能有其他建议)取决于您的特定应用程序。祝你好运。

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). 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. As written you were using a file named file instead of the name passed in as the file parameter.
  • 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. I just assigned it a value of 0 to allow it to compile. 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. (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).
  • line 14 - you need to assign the return value of the function to a variable. At least you did with my gfortran compiler. 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. 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. 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. i.e. line 14 would look like this:

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 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. 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. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文