如何从外部 .txt 文件读取数据,将它们存储在数组中,过滤并将它们写入 Fortran 90 中的新文件?
How to read data from external .txt file, store them in arrays, filter and write them in a new file in Fortran 90?
我在该网站上阅读过类似的已解决问题,但它们确实对我有帮助!所以,很抱歉提出类似的问题。
我有以下名为“Asteroids_Numbered.txt”的 .txt 文件(该文件有很多行,即 607013,但为了简单起见,我少放了很多行):
Num Name Epoch a e i w Node M H G Ref
------ ----------------- ----- ---------- ---------- --------- --------- --------- ----------- ----- ----- ----------
1 Ceres 59600 2.7660431 0.07850100 10.58769 73.63704 80.26860 291.3755993 3.54 0.12 JPL 48
2 Pallas 59600 2.7711069 0.22999297 34.92530 310.69725 172.91657 272.4799259 4.22 0.11 JPL 49
3 Juno 59600 2.6687911 0.25688702 12.99186 247.94173 169.84780 261.2986327 5.28 0.32 JPL 123
4 Vesta 59600 2.3612665 0.08823418 7.14172 151.09094 103.80392 7.0315225 3.40 0.32 JPL 36
5 Astraea 59600 2.5751766 0.19009936 5.36762 358.74039 141.57036 160.9820880 6.99 0.15 JPL 125
6 Hebe 59600 2.4256657 0.20306151 14.73873 239.50547 138.64097 347.4991368 5.65 0.24 JPL 100
7 Iris 59600 2.3866161 0.22949924 5.51768 145.34355 259.52553 47.6423152 5.61 0.15 JPL 119
8 Flora 59600 2.2017319 0.15606719 5.88872 285.55022 110.87251 136.2585358 6.54 0.28 JPL 127
9 Metis 59600 2.3852921 0.12356142 5.57695 6.16423 68.89958 184.5626181 6.37 0.17 JPL 128
10 Hygiea 59600 3.1418676 0.11162598 3.83093 312.49331 283.18419 328.8968591 5.55 0.15 JPL 105
11 Parthenope 59600 2.4532814 0.09954681 4.63165 195.59824 125.52829 175.4211548 6.60 0.15 JPL 118
12 Victoria 59600 2.3337809 0.22074254 8.37333 69.66955 235.36878 49.7506630 7.31 0.22 JPL 131
13 Egeria 59600 2.5765835 0.08544364 16.53450 80.14708 43.20673 66.2442983 6.84 0.15 JPL 103
14 Irene 59600 2.5859176 0.16587880 9.12082 97.71349 86.11601 42.0351479 6.53 0.15 JPL 96
15 Eunomia 59600 2.6440754 0.18662534 11.75200 98.63169 292.92610 152.5002319 5.41 0.23 JPL 85
16 Psyche 59600 2.9244847 0.13392662 3.09684 229.21980 150.03218 125.1275316 6.06 0.20 JPL 90
17 Thetis 59600 2.4706187 0.13286003 5.59276 135.80674 125.54282 197.5734224 7.76 0.15 JPL 125
18 Melpomene 59600 2.2957889 0.21790920 10.13249 228.11923 150.36173 190.3739342 6.53 0.25 JPL 116
19 Fortuna 59600 2.4429040 0.15701789 1.57276 182.47214 211.04422 95.0887535 7.38 0.10 JPL 142
20 Massalia 59600 2.4088126 0.14306413 0.70880 257.55922 205.97388 20.5136762 6.56 0.25 JPL 118
21 Lutetia 59600 2.4351916 0.16354177 3.06364 250.15544 80.85386 243.3813245 7.52 0.11 JPL 118
22 Kalliope 59600 2.9102024 0.09838131 13.70049 357.60063 65.99349 33.4836574 6.51 0.21 JPL 111
我如何创建一个程序来读取这个文件,将数据存储在一维数组中(每种数据一个,所以我想得到 12 个数组)然后根据一些条件过滤它们,例如倾角值 (i) 小于 2deg?
最后,如何将过滤后的数据存储到与原始文件格式相同的新文件中?
这是我的代码(只包含阅读部分):
program Read_write_ephemerides_Main
implicit none
!Declarations
character*100 :: input_path,input_filename, output_path, output_filename
double precision, dimension(:,:), allocatable :: Epoch_TDB, a_AU, e, i_deg, w_deg, Node_deg, M_deg, H_mag, G
character*30, dimension(4) :: str_output
character, dimension (:,:), allocatable :: Name, Ref
integer :: i,iu, i_counter
integer, dimension (:,:), allocatable :: Number
logical :: bContinue
! Definition of constants, paths names and file names
iu = 10
input_path = 'D:\MSc_Aerospace_Eng\Thesis\Fortran_projects\Read_write_ephemerides\InputFiles\'
input_filename = 'Asteroids_Numbered.txt'
!output_path = 'D:\MSc_Aerospace_Eng\Thesis\Fortran_projects\Read_write_ephemerides\OutputFiles\'
!output_filename = 'prova_ast_num.txt'
! Reading of Asteroids_numbered file
open(unit = iu, file = trim(input_path) // trim(input_filename), status='old', &
access = 'sequential',form = 'formatted', action='read')
read(iu,'(//)') ! skip first 2 lines
read(iu,'(i10,a25,f10.0,6(f12.8),2(f5.4),f5.4)') Number, Name, Epoch_TDB, a_AU, e, i_deg, w_deg, Node_deg, M_deg, H_mag, G, Ref
close(unit = iu,status='keep')
! Creation of output file
!open(unit = iu, file = trim(output_path) // trim(output_filename1), status = 'unknown', action = 'write')
!write(iu,'(i10,a25,f10.0,6(f12.8),2(f5.4),f5.4)') Number, Name, Epoch_TDB, a_AU, e, i_deg, w_deg, Node_deg, M_deg, H_mag, G, Ref
!close(unit = iu,status='keep')
!
stop
end program Read_write_ephemerides_Main
编辑:代码已更新
有用的说明:我在 Microsoft Visual Studio 2022
中使用 Intel Fortran 编译器
要扩展@HighPerformanceMark 的评论,最好的办法是定义一个 Asteroid
类型,它包含有关小行星的所有信息,然后创建一个 Asteroid
数组.
Asteroid
类型
Asteroid
类型最初应该只包含有关小行星的数据,
type :: Asteroid
integer :: num
character(:), allocatable :: name
integer :: epoch
real(dp) :: a
real(dp) :: e
real(dp) :: i
real(dp) :: w
real(dp) :: node
real(dp) :: m
real(dp) :: h
real(dp) :: g
character(:), allocatable :: ref_name
integer :: ref_number
end type
其中 dp
defines double precision.
这允许您拥有一个 Asteroid
数组,例如
type(Asteroid) :: asteroids(22)
asteroids(1) = Asteroid(1, "Ceres", ..., "JPL", 48)
...
asteroids(22) = Asteroid(22, "Kalliope", ..., "JPL", 111)
write(*,*) asteroids(1)%name ! Writes "Ceres".
读写Asteroid
s
您希望能够 read
和 write
小行星往返于文件,您可以使用 user defined input/output 来实现。为此,您需要一个 read
和 Asteroid
的子程序,例如
subroutine read_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(inout) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
character(100) :: name
character(100) :: ref_name
read(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& ref_name, &
& this%ref_number
this%name = trim(name)
this%ref_name = trim(ref_name)
end subroutine
和另一个 write
和 Asteroid
,例如
subroutine write_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(in) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& this%name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& this%ref_name, &
& this%ref_number
end subroutine
您还需要将 bindings
添加到 Asteroid
类型,以便它知道使用 read_Asteroid
和 write_Asteroid
进行读写。这看起来像
type :: Asteroid
integer :: num
...
integer :: ref_number
contains
! `read` binding.
generic :: read(formatted) => read_Asteroid
procedure :: read_Asteroid
! `write` binding.
generic :: write(formatted) => write_Asteroid
procedure :: write_Asteroid
end type
N.B。因为 Asteroid
类型有 allocatable
个组件(name
和 ref_name
),这些组件不是由 read
语句分配的,所以在写 read_Asteroid
. 可以用来读取allocatable
s;首先读取到超大缓冲区,然后将数据复制到 allocatable
变量。 (感谢@francescalus 在这里指出我的代码以前的问题)。
现在可以直接 read
和 write
小行星,例如
character(1000) :: line
type(Asteroid) :: Ceres
line = "1 Ceres 59600 2.766 0.07850 10.58 73.63 80.26 291.3 3.54 0.12 JPL 48"
read(line, *) Ceres
write(*, *) Ceres
示例代码
综上所述,这是一个示例代码,它读取一个充满小行星的文件,然后用 i < 2
:
写入这些文件
module asteroid_module
implicit none
! Define `dp`, which defines double precision.
integer, parameter :: dp = selected_real_kind(15, 307)
! Define the `Asteroid` type.
type :: Asteroid
integer :: num
character(:), allocatable :: name
integer :: epoch
real(dp) :: a
real(dp) :: e
real(dp) :: i
real(dp) :: w
real(dp) :: node
real(dp) :: m
real(dp) :: h
real(dp) :: g
character(:), allocatable :: ref_name
integer :: ref_number
contains
! `read` binding.
generic :: read(formatted) => read_Asteroid
procedure :: read_Asteroid
! `write` binding.
generic :: write(formatted) => write_Asteroid
procedure :: write_Asteroid
end type
contains
! Define how to `read` an `Asteroid`.
subroutine read_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(inout) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
character(100) :: name
character(100) :: ref_name
read(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& ref_name, &
& this%ref_number
this%name = trim(name)
this%ref_name = trim(ref_name)
end subroutine
! Define how to `write` an `Asteroid`.
subroutine write_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(in) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& this%name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& this%ref_name, &
& this%ref_number
end subroutine
end module
program example
use asteroid_module
implicit none
character(1000) :: line
integer :: iostat
integer :: file_length
type(Asteroid), allocatable :: asteroids(:)
integer :: i
! Count the number of lines in the file.
file_length = 0
open(10, file="input.txt")
do
read(10, '(A)',iostat=iostat) line
if (iostat/=0) then
exit
endif
file_length = file_length + 1
enddo
close(10)
! Allocate the array to hold the asteroids.
allocate(asteroids(file_length-2))
! Read the asteroids into the array.
open(10, file="input.txt")
read(10, '(A)') line
read(10, '(A)') line
do i=1,size(asteroids)
read(10, '(A)') line
read(line, *) asteroids(i)
enddo
close(10)
! Write the asteroids with `i` < 2 to a file.
open(10, file="output.txt")
do i=1,size(asteroids)
if (asteroids(i)%i < 2.0_dp) then
write(10,*) asteroids(i)
endif
enddo
close(10)
end program
让我们在继续下一部分之前解决一件事:如果这只是一个“过滤”任务,请将其视为过滤任务。
在 Fortran 2018 中,这可能很简单
implicit none
character(1234) line
integer iostat, nchars
do
read (*,'(A)',iostat=iostat,size=nchars) line
if (iostat.lt.0) exit
if (KEEP_LINE) print *, line(:nchars) ! Implement conditional
end do
end program
(如果您的编译器不是 Fortran 2018 编译器,您需要将其复杂化。)该程序在 Unix-sense:
中充当过滤器
./program < input_file > output_file
对于这个问题,过滤器类似于“传递前两行;传递第六列数字小于 2 的后面的行”。我将把确切的规范留作练习,注意我们可以用
完成这项工作
awk 'NR<3||<2' < input_file > output_file
请注意,您可以简单地 extract the sixth column 而无需为每一列创建变量 - 或者您可以注意到它是 line(52:)
的第一列。
这就是过滤掉的方式。让我们看看如何创建数据结构并在 Fortran 程序中使用它做一些事情。
正如 High Performance Mark 评论的那样,非常梦幻 expanded on 我们可以为这个“数据 table” 创建一个派生类型(如果所有列都是相同的数据类型,我们可能只需要2 级内在类型,尽管即使在这种情况下派生类型也有帮助):
type asteroids_t
integer :: num
character(18) :: name
integer :: epoch
real :: a, e, i, w, node, m, h, g
character(10) :: ref
end type asteroids_t
(根据需要设置每个组件的种类参数,但实数可能是双精度)
我们有一个输入和输出格式:
character(*), parameter :: FMT='(i6,a,i7,f10.7,f11.8,3f10.5,f12.7,2f6.2,a)'
(请注意,我们不能对输入使用 list-directed 格式,因为最后一列的字符中有一个 space。同样,解决这个问题是一个练习。)
假设我们有一个适当大小的数组(请参阅有关读取行数未知的文件的一般问题,或此处的 veryreverie 的回答以获取详细信息)我们就可以开始了。为了清楚起见,我将使用明确的大小。
type(asteroids_t) asteroids(NUMBER_OF_ASTEROIDS)
integer, allocatable :: pass(:)
read FMT, asteroids
... ! Work, including setting pass for filter
print FMT, asteroids(pass)
将所有这些放在一起形成一个 quick-and-dirty 程序:
implicit none
type asteroids_t
integer :: num
character(18) :: name
integer :: epoch
real(KIND(0d0)) :: a, e, i, w, node, m, h, g
character(10) :: ref
end type asteroids_t
type(asteroids_t) :: asteroids(22)
character(118) :: header(2)
character(*), parameter :: FMT='(i6,a,i7,f10.7,f11.8,3f10.5,f12.7,2f6.2,a)'
integer :: i
read '(A)', header
print '(A)', header
read FMT, asteroids
print FMT, asteroids(PACK([(i,i=1,SIZE(asteroids))], asteroids%i<2))
end program
需要注意的关键点是我们可以用“正常”处理我们的派生类型input/output:项目asteroids
被扩展为数组元素,然后每个数组元素被扩展为组件。因为我们的派生类型没有私有、指针或 allocatable 组件,所以我们可以使用这种简单的处理形式。
作为更高级的 material,请注意此处示例中的“幻数”。我们已经知道如何删除神奇的小行星计数 (22) 以及 header 行(2 和 118)的神奇数字和长度。但也许我们担心那些字符组件(18 和 10)的长度。
我们的数据结构与输入数据文件的形式紧密耦合,但是如果我们有两个名称长度不同的数据集怎么办?重写或复制我们的派生类型来处理这个问题确实很痛苦。让我们来解决这个问题:
type asteroids_t(len_name, len_ref)
integer, len :: len_name=18, len_ref=10
integer :: num
character(len_name) :: name
integer :: epoch
real(KIND(0d0)) :: a, e, i, w, node, m, h, g
character(len_ref) :: ref
end type asteroids_t
type(asteroids_t) asteroids_set_1(22)
type(asteroids(25,8)) asteroids_set_2(22)
! There's no magic character length in FMT
read FMT, asteroids_set_1
read FMT, asteroids_set_2
列宽甚至可以推迟到 run-time 处解决(未显示)。您可以在其他地方更详细地阅读这些 参数化 派生类型。
我在该网站上阅读过类似的已解决问题,但它们确实对我有帮助!所以,很抱歉提出类似的问题。
我有以下名为“Asteroids_Numbered.txt”的 .txt 文件(该文件有很多行,即 607013,但为了简单起见,我少放了很多行):
Num Name Epoch a e i w Node M H G Ref
------ ----------------- ----- ---------- ---------- --------- --------- --------- ----------- ----- ----- ----------
1 Ceres 59600 2.7660431 0.07850100 10.58769 73.63704 80.26860 291.3755993 3.54 0.12 JPL 48
2 Pallas 59600 2.7711069 0.22999297 34.92530 310.69725 172.91657 272.4799259 4.22 0.11 JPL 49
3 Juno 59600 2.6687911 0.25688702 12.99186 247.94173 169.84780 261.2986327 5.28 0.32 JPL 123
4 Vesta 59600 2.3612665 0.08823418 7.14172 151.09094 103.80392 7.0315225 3.40 0.32 JPL 36
5 Astraea 59600 2.5751766 0.19009936 5.36762 358.74039 141.57036 160.9820880 6.99 0.15 JPL 125
6 Hebe 59600 2.4256657 0.20306151 14.73873 239.50547 138.64097 347.4991368 5.65 0.24 JPL 100
7 Iris 59600 2.3866161 0.22949924 5.51768 145.34355 259.52553 47.6423152 5.61 0.15 JPL 119
8 Flora 59600 2.2017319 0.15606719 5.88872 285.55022 110.87251 136.2585358 6.54 0.28 JPL 127
9 Metis 59600 2.3852921 0.12356142 5.57695 6.16423 68.89958 184.5626181 6.37 0.17 JPL 128
10 Hygiea 59600 3.1418676 0.11162598 3.83093 312.49331 283.18419 328.8968591 5.55 0.15 JPL 105
11 Parthenope 59600 2.4532814 0.09954681 4.63165 195.59824 125.52829 175.4211548 6.60 0.15 JPL 118
12 Victoria 59600 2.3337809 0.22074254 8.37333 69.66955 235.36878 49.7506630 7.31 0.22 JPL 131
13 Egeria 59600 2.5765835 0.08544364 16.53450 80.14708 43.20673 66.2442983 6.84 0.15 JPL 103
14 Irene 59600 2.5859176 0.16587880 9.12082 97.71349 86.11601 42.0351479 6.53 0.15 JPL 96
15 Eunomia 59600 2.6440754 0.18662534 11.75200 98.63169 292.92610 152.5002319 5.41 0.23 JPL 85
16 Psyche 59600 2.9244847 0.13392662 3.09684 229.21980 150.03218 125.1275316 6.06 0.20 JPL 90
17 Thetis 59600 2.4706187 0.13286003 5.59276 135.80674 125.54282 197.5734224 7.76 0.15 JPL 125
18 Melpomene 59600 2.2957889 0.21790920 10.13249 228.11923 150.36173 190.3739342 6.53 0.25 JPL 116
19 Fortuna 59600 2.4429040 0.15701789 1.57276 182.47214 211.04422 95.0887535 7.38 0.10 JPL 142
20 Massalia 59600 2.4088126 0.14306413 0.70880 257.55922 205.97388 20.5136762 6.56 0.25 JPL 118
21 Lutetia 59600 2.4351916 0.16354177 3.06364 250.15544 80.85386 243.3813245 7.52 0.11 JPL 118
22 Kalliope 59600 2.9102024 0.09838131 13.70049 357.60063 65.99349 33.4836574 6.51 0.21 JPL 111
我如何创建一个程序来读取这个文件,将数据存储在一维数组中(每种数据一个,所以我想得到 12 个数组)然后根据一些条件过滤它们,例如倾角值 (i) 小于 2deg? 最后,如何将过滤后的数据存储到与原始文件格式相同的新文件中?
这是我的代码(只包含阅读部分):
program Read_write_ephemerides_Main
implicit none
!Declarations
character*100 :: input_path,input_filename, output_path, output_filename
double precision, dimension(:,:), allocatable :: Epoch_TDB, a_AU, e, i_deg, w_deg, Node_deg, M_deg, H_mag, G
character*30, dimension(4) :: str_output
character, dimension (:,:), allocatable :: Name, Ref
integer :: i,iu, i_counter
integer, dimension (:,:), allocatable :: Number
logical :: bContinue
! Definition of constants, paths names and file names
iu = 10
input_path = 'D:\MSc_Aerospace_Eng\Thesis\Fortran_projects\Read_write_ephemerides\InputFiles\'
input_filename = 'Asteroids_Numbered.txt'
!output_path = 'D:\MSc_Aerospace_Eng\Thesis\Fortran_projects\Read_write_ephemerides\OutputFiles\'
!output_filename = 'prova_ast_num.txt'
! Reading of Asteroids_numbered file
open(unit = iu, file = trim(input_path) // trim(input_filename), status='old', &
access = 'sequential',form = 'formatted', action='read')
read(iu,'(//)') ! skip first 2 lines
read(iu,'(i10,a25,f10.0,6(f12.8),2(f5.4),f5.4)') Number, Name, Epoch_TDB, a_AU, e, i_deg, w_deg, Node_deg, M_deg, H_mag, G, Ref
close(unit = iu,status='keep')
! Creation of output file
!open(unit = iu, file = trim(output_path) // trim(output_filename1), status = 'unknown', action = 'write')
!write(iu,'(i10,a25,f10.0,6(f12.8),2(f5.4),f5.4)') Number, Name, Epoch_TDB, a_AU, e, i_deg, w_deg, Node_deg, M_deg, H_mag, G, Ref
!close(unit = iu,status='keep')
!
stop
end program Read_write_ephemerides_Main
编辑:代码已更新 有用的说明:我在 Microsoft Visual Studio 2022
中使用 Intel Fortran 编译器要扩展@HighPerformanceMark 的评论,最好的办法是定义一个 Asteroid
类型,它包含有关小行星的所有信息,然后创建一个 Asteroid
数组.
Asteroid
类型
Asteroid
类型最初应该只包含有关小行星的数据,
type :: Asteroid
integer :: num
character(:), allocatable :: name
integer :: epoch
real(dp) :: a
real(dp) :: e
real(dp) :: i
real(dp) :: w
real(dp) :: node
real(dp) :: m
real(dp) :: h
real(dp) :: g
character(:), allocatable :: ref_name
integer :: ref_number
end type
其中 dp
defines double precision.
这允许您拥有一个 Asteroid
数组,例如
type(Asteroid) :: asteroids(22)
asteroids(1) = Asteroid(1, "Ceres", ..., "JPL", 48)
...
asteroids(22) = Asteroid(22, "Kalliope", ..., "JPL", 111)
write(*,*) asteroids(1)%name ! Writes "Ceres".
读写Asteroid
s
您希望能够 read
和 write
小行星往返于文件,您可以使用 user defined input/output 来实现。为此,您需要一个 read
和 Asteroid
的子程序,例如
subroutine read_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(inout) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
character(100) :: name
character(100) :: ref_name
read(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& ref_name, &
& this%ref_number
this%name = trim(name)
this%ref_name = trim(ref_name)
end subroutine
和另一个 write
和 Asteroid
,例如
subroutine write_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(in) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& this%name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& this%ref_name, &
& this%ref_number
end subroutine
您还需要将 bindings
添加到 Asteroid
类型,以便它知道使用 read_Asteroid
和 write_Asteroid
进行读写。这看起来像
type :: Asteroid
integer :: num
...
integer :: ref_number
contains
! `read` binding.
generic :: read(formatted) => read_Asteroid
procedure :: read_Asteroid
! `write` binding.
generic :: write(formatted) => write_Asteroid
procedure :: write_Asteroid
end type
N.B。因为 Asteroid
类型有 allocatable
个组件(name
和 ref_name
),这些组件不是由 read
语句分配的,所以在写 read_Asteroid
. allocatable
s;首先读取到超大缓冲区,然后将数据复制到 allocatable
变量。 (感谢@francescalus 在这里指出我的代码以前的问题)。
现在可以直接 read
和 write
小行星,例如
character(1000) :: line
type(Asteroid) :: Ceres
line = "1 Ceres 59600 2.766 0.07850 10.58 73.63 80.26 291.3 3.54 0.12 JPL 48"
read(line, *) Ceres
write(*, *) Ceres
示例代码
综上所述,这是一个示例代码,它读取一个充满小行星的文件,然后用 i < 2
:
module asteroid_module
implicit none
! Define `dp`, which defines double precision.
integer, parameter :: dp = selected_real_kind(15, 307)
! Define the `Asteroid` type.
type :: Asteroid
integer :: num
character(:), allocatable :: name
integer :: epoch
real(dp) :: a
real(dp) :: e
real(dp) :: i
real(dp) :: w
real(dp) :: node
real(dp) :: m
real(dp) :: h
real(dp) :: g
character(:), allocatable :: ref_name
integer :: ref_number
contains
! `read` binding.
generic :: read(formatted) => read_Asteroid
procedure :: read_Asteroid
! `write` binding.
generic :: write(formatted) => write_Asteroid
procedure :: write_Asteroid
end type
contains
! Define how to `read` an `Asteroid`.
subroutine read_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(inout) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
character(100) :: name
character(100) :: ref_name
read(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& ref_name, &
& this%ref_number
this%name = trim(name)
this%ref_name = trim(ref_name)
end subroutine
! Define how to `write` an `Asteroid`.
subroutine write_Asteroid(this, unit, iotype, v_list, iostat, iomsg)
class(Asteroid), intent(in) :: this
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, *, iostat=iostat, iomsg=iomsg) &
& this%num, &
& this%name, &
& this%epoch, &
& this%a, &
& this%e, &
& this%i, &
& this%w, &
& this%node, &
& this%m, &
& this%h, &
& this%g, &
& this%ref_name, &
& this%ref_number
end subroutine
end module
program example
use asteroid_module
implicit none
character(1000) :: line
integer :: iostat
integer :: file_length
type(Asteroid), allocatable :: asteroids(:)
integer :: i
! Count the number of lines in the file.
file_length = 0
open(10, file="input.txt")
do
read(10, '(A)',iostat=iostat) line
if (iostat/=0) then
exit
endif
file_length = file_length + 1
enddo
close(10)
! Allocate the array to hold the asteroids.
allocate(asteroids(file_length-2))
! Read the asteroids into the array.
open(10, file="input.txt")
read(10, '(A)') line
read(10, '(A)') line
do i=1,size(asteroids)
read(10, '(A)') line
read(line, *) asteroids(i)
enddo
close(10)
! Write the asteroids with `i` < 2 to a file.
open(10, file="output.txt")
do i=1,size(asteroids)
if (asteroids(i)%i < 2.0_dp) then
write(10,*) asteroids(i)
endif
enddo
close(10)
end program
让我们在继续下一部分之前解决一件事:如果这只是一个“过滤”任务,请将其视为过滤任务。
在 Fortran 2018 中,这可能很简单
implicit none
character(1234) line
integer iostat, nchars
do
read (*,'(A)',iostat=iostat,size=nchars) line
if (iostat.lt.0) exit
if (KEEP_LINE) print *, line(:nchars) ! Implement conditional
end do
end program
(如果您的编译器不是 Fortran 2018 编译器,您需要将其复杂化。)该程序在 Unix-sense:
中充当过滤器./program < input_file > output_file
对于这个问题,过滤器类似于“传递前两行;传递第六列数字小于 2 的后面的行”。我将把确切的规范留作练习,注意我们可以用
完成这项工作awk 'NR<3||<2' < input_file > output_file
请注意,您可以简单地 extract the sixth column 而无需为每一列创建变量 - 或者您可以注意到它是 line(52:)
的第一列。
这就是过滤掉的方式。让我们看看如何创建数据结构并在 Fortran 程序中使用它做一些事情。
正如 High Performance Mark 评论的那样,非常梦幻 expanded on 我们可以为这个“数据 table” 创建一个派生类型(如果所有列都是相同的数据类型,我们可能只需要2 级内在类型,尽管即使在这种情况下派生类型也有帮助):
type asteroids_t
integer :: num
character(18) :: name
integer :: epoch
real :: a, e, i, w, node, m, h, g
character(10) :: ref
end type asteroids_t
(根据需要设置每个组件的种类参数,但实数可能是双精度)
我们有一个输入和输出格式:
character(*), parameter :: FMT='(i6,a,i7,f10.7,f11.8,3f10.5,f12.7,2f6.2,a)'
(请注意,我们不能对输入使用 list-directed 格式,因为最后一列的字符中有一个 space。同样,解决这个问题是一个练习。)
假设我们有一个适当大小的数组(请参阅有关读取行数未知的文件的一般问题,或此处的 veryreverie 的回答以获取详细信息)我们就可以开始了。为了清楚起见,我将使用明确的大小。
type(asteroids_t) asteroids(NUMBER_OF_ASTEROIDS)
integer, allocatable :: pass(:)
read FMT, asteroids
... ! Work, including setting pass for filter
print FMT, asteroids(pass)
将所有这些放在一起形成一个 quick-and-dirty 程序:
implicit none
type asteroids_t
integer :: num
character(18) :: name
integer :: epoch
real(KIND(0d0)) :: a, e, i, w, node, m, h, g
character(10) :: ref
end type asteroids_t
type(asteroids_t) :: asteroids(22)
character(118) :: header(2)
character(*), parameter :: FMT='(i6,a,i7,f10.7,f11.8,3f10.5,f12.7,2f6.2,a)'
integer :: i
read '(A)', header
print '(A)', header
read FMT, asteroids
print FMT, asteroids(PACK([(i,i=1,SIZE(asteroids))], asteroids%i<2))
end program
需要注意的关键点是我们可以用“正常”处理我们的派生类型input/output:项目asteroids
被扩展为数组元素,然后每个数组元素被扩展为组件。因为我们的派生类型没有私有、指针或 allocatable 组件,所以我们可以使用这种简单的处理形式。
作为更高级的 material,请注意此处示例中的“幻数”。我们已经知道如何删除神奇的小行星计数 (22) 以及 header 行(2 和 118)的神奇数字和长度。但也许我们担心那些字符组件(18 和 10)的长度。
我们的数据结构与输入数据文件的形式紧密耦合,但是如果我们有两个名称长度不同的数据集怎么办?重写或复制我们的派生类型来处理这个问题确实很痛苦。让我们来解决这个问题:
type asteroids_t(len_name, len_ref)
integer, len :: len_name=18, len_ref=10
integer :: num
character(len_name) :: name
integer :: epoch
real(KIND(0d0)) :: a, e, i, w, node, m, h, g
character(len_ref) :: ref
end type asteroids_t
type(asteroids_t) asteroids_set_1(22)
type(asteroids(25,8)) asteroids_set_2(22)
! There's no magic character length in FMT
read FMT, asteroids_set_1
read FMT, asteroids_set_2
列宽甚至可以推迟到 run-time 处解决(未显示)。您可以在其他地方更详细地阅读这些 参数化 派生类型。