在字符串匹配后从下一行将值读入 Fortran 数组
Read values into a Fortran array from the next line after a string match
我正在尝试解析 Abaqus 输入文件并将所需的节点读入数组。我首先尝试比较一个字符串,如果它匹配,那么我想从下一行读入未知数量的整数值。
文本文件的示例如下所示。我想读取 nset=bf1:
下的值
** PARTS
*Part, name=Beam
*Nset, nset=bf1, instance=Beam-1
1, 2, 5, 43, 45, 48, 49, 50, 105, 106, 1189, 1190, 1191, 1192, 1193, 1194
5275, 5276, 5277, 5278
*Elset, elset=_Surf-3_SNEG, internal, instance=Beam-1
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
我现在正在做的是这个
program nset_parser
implicit none
integer, parameter :: rk = selected_real_kind(10,40)
real(kind=rk), dimension(:), allocatable :: values
integer :: ios,l,val, n, nlines
character(len=80) :: file_name = 'code_check.inp'
character (len=*), parameter :: search_str = "bf1"
character(len=5096) :: input_line, line
character(len=10) :: keyword, get_keyword
logical :: h_nset=.false.
l = len(search_str)
open(unit=10, file=file_name, status="old", action='read') ! open file
do
read(10, '(a)',iostat=ios) input_line ! read the lines
if (ios /=0) exit
input_line = adjustl(input_line)
if (input_line(1:1) == '*' .and. input_line(2:2) /= '*') then ! check if it is a comment line or data line
keyword=get_keyword(input_line)
if (trim(keyword) == 'Nset') then
read(input_line,'(a)') line
val = index(line, search_str)
if (val /= 0) then
h_nset=.true.
print*, line(val:val+l-1)
end if
end if
end if
end do
close(10)
end program nset_parser
但在这之后我有点难过。
我重写了我的答案以使其更笼统。此代码将处理带有“nset=bf1”的行与任何其他以“*”开头的行之间的多行整数。
因为整数行的长度可能不同,所以二维整数数组是不合适的。相反,我将所有整数存储在一维数组中,并存储每个新行的数组索引。
为避免读取所有内容两次,如有必要,我会即时增加数组的大小。
请注意,如果您的编译器不支持 MOVE_ALLOC (Fortran 2003),那么您只需将每个调用替换为 'deallocate, allocate, deallocate' 集。
program nset_parser
implicit none
INTEGER :: ios,l,val, i, n, nlines, comma_idx, next_idx
INTEGER, ALLOCATABLE :: my_ints(:), line_starts(:), work_ints(:)
character(len=80) :: file_name = 'code_check.inp'
character (len=*), parameter :: search_str = "bf1"
character(len=5096) :: input_line
character(len=10) :: keyword
logical :: h_nset=.false.
l = len(search_str)
ALLOCATE(line_starts(10), my_ints(1000))
next_idx = 1
nlines = 0
open(unit=10, file=file_name, status="old", action='read') ! open file
DO
! Read and adjust the line
read(10, '(a)',iostat=ios) input_line
if (ios /=0) exit
input_line = adjustl(input_line)
! Ignore blank lines
IF(LEN(TRIM(input_line))==0) CYCLE
! This section picks up any line beginning *
! and either sets or unsets h_nset
IF (input_line(1:1) == '*' .AND. input_line(2:2) /= '*') THEN ! check if it is a comment line or data line
! Use of keyword below is a little unsafe if
! keywords can be longer than 10 chars (the length of 'keyword')
! Instead, better to check if input_line(2:5) == Nset
! I left it like this in case you want to be able to check multiple
! keywords or something.
comma_idx = INDEX(input_line, ',')
keyword = input_line(2:comma_idx-1)
IF (TRIM(keyword) == 'Nset') THEN
val = INDEX(input_line, search_str)
IF (val /= 0) THEN
h_nset=.TRUE. ! Switch on when nset=bf1 line is found
CYCLE
END IF
END IF
h_nset = .FALSE. ! Only reach this point if we are in a * line which isn't nset=bf1
END IF
IF(h_nset) THEN
n = COUNT(TRANSFER(input_line, 'a', LEN(input_line)) == ",") + 1 !cast to chars and count occurrence of comma
nlines = nlines + 1
! Double size of arrays on the fly if necessary
IF(nlines > SIZE(line_starts)) THEN
ALLOCATE(work_ints(nlines * 2))
work_ints(1:SIZE(line_starts)) = line_starts
CALL MOVE_ALLOC(work_ints, line_starts)
END IF
IF(next_idx+n > SIZE(my_ints)) THEN
ALLOCATE(work_ints(2*SIZE(my_ints)))
work_ints(1:SIZE(my_ints)) = my_ints
CALL MOVE_ALLOC(work_ints, my_ints)
END IF
line_starts(nlines) = next_idx
READ(input_line, *) my_ints(next_idx:next_idx+n-1)
next_idx = next_idx + n
END IF
END DO
close(10)
! This helps with the printing below
line_starts(nlines+1) = line_starts(nlines) + n
DO i=1, nlines
PRINT *, my_ints(line_starts(i):line_starts(i+1)-1)
END DO
end program nset_parser
我正在尝试解析 Abaqus 输入文件并将所需的节点读入数组。我首先尝试比较一个字符串,如果它匹配,那么我想从下一行读入未知数量的整数值。
文本文件的示例如下所示。我想读取 nset=bf1:
下的值** PARTS
*Part, name=Beam
*Nset, nset=bf1, instance=Beam-1
1, 2, 5, 43, 45, 48, 49, 50, 105, 106, 1189, 1190, 1191, 1192, 1193, 1194
5275, 5276, 5277, 5278
*Elset, elset=_Surf-3_SNEG, internal, instance=Beam-1
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
我现在正在做的是这个
program nset_parser
implicit none
integer, parameter :: rk = selected_real_kind(10,40)
real(kind=rk), dimension(:), allocatable :: values
integer :: ios,l,val, n, nlines
character(len=80) :: file_name = 'code_check.inp'
character (len=*), parameter :: search_str = "bf1"
character(len=5096) :: input_line, line
character(len=10) :: keyword, get_keyword
logical :: h_nset=.false.
l = len(search_str)
open(unit=10, file=file_name, status="old", action='read') ! open file
do
read(10, '(a)',iostat=ios) input_line ! read the lines
if (ios /=0) exit
input_line = adjustl(input_line)
if (input_line(1:1) == '*' .and. input_line(2:2) /= '*') then ! check if it is a comment line or data line
keyword=get_keyword(input_line)
if (trim(keyword) == 'Nset') then
read(input_line,'(a)') line
val = index(line, search_str)
if (val /= 0) then
h_nset=.true.
print*, line(val:val+l-1)
end if
end if
end if
end do
close(10)
end program nset_parser
但在这之后我有点难过。
我重写了我的答案以使其更笼统。此代码将处理带有“nset=bf1”的行与任何其他以“*”开头的行之间的多行整数。
因为整数行的长度可能不同,所以二维整数数组是不合适的。相反,我将所有整数存储在一维数组中,并存储每个新行的数组索引。
为避免读取所有内容两次,如有必要,我会即时增加数组的大小。
请注意,如果您的编译器不支持 MOVE_ALLOC (Fortran 2003),那么您只需将每个调用替换为 'deallocate, allocate, deallocate' 集。
program nset_parser
implicit none
INTEGER :: ios,l,val, i, n, nlines, comma_idx, next_idx
INTEGER, ALLOCATABLE :: my_ints(:), line_starts(:), work_ints(:)
character(len=80) :: file_name = 'code_check.inp'
character (len=*), parameter :: search_str = "bf1"
character(len=5096) :: input_line
character(len=10) :: keyword
logical :: h_nset=.false.
l = len(search_str)
ALLOCATE(line_starts(10), my_ints(1000))
next_idx = 1
nlines = 0
open(unit=10, file=file_name, status="old", action='read') ! open file
DO
! Read and adjust the line
read(10, '(a)',iostat=ios) input_line
if (ios /=0) exit
input_line = adjustl(input_line)
! Ignore blank lines
IF(LEN(TRIM(input_line))==0) CYCLE
! This section picks up any line beginning *
! and either sets or unsets h_nset
IF (input_line(1:1) == '*' .AND. input_line(2:2) /= '*') THEN ! check if it is a comment line or data line
! Use of keyword below is a little unsafe if
! keywords can be longer than 10 chars (the length of 'keyword')
! Instead, better to check if input_line(2:5) == Nset
! I left it like this in case you want to be able to check multiple
! keywords or something.
comma_idx = INDEX(input_line, ',')
keyword = input_line(2:comma_idx-1)
IF (TRIM(keyword) == 'Nset') THEN
val = INDEX(input_line, search_str)
IF (val /= 0) THEN
h_nset=.TRUE. ! Switch on when nset=bf1 line is found
CYCLE
END IF
END IF
h_nset = .FALSE. ! Only reach this point if we are in a * line which isn't nset=bf1
END IF
IF(h_nset) THEN
n = COUNT(TRANSFER(input_line, 'a', LEN(input_line)) == ",") + 1 !cast to chars and count occurrence of comma
nlines = nlines + 1
! Double size of arrays on the fly if necessary
IF(nlines > SIZE(line_starts)) THEN
ALLOCATE(work_ints(nlines * 2))
work_ints(1:SIZE(line_starts)) = line_starts
CALL MOVE_ALLOC(work_ints, line_starts)
END IF
IF(next_idx+n > SIZE(my_ints)) THEN
ALLOCATE(work_ints(2*SIZE(my_ints)))
work_ints(1:SIZE(my_ints)) = my_ints
CALL MOVE_ALLOC(work_ints, my_ints)
END IF
line_starts(nlines) = next_idx
READ(input_line, *) my_ints(next_idx:next_idx+n-1)
next_idx = next_idx + n
END IF
END DO
close(10)
! This helps with the printing below
line_starts(nlines+1) = line_starts(nlines) + n
DO i=1, nlines
PRINT *, my_ints(line_starts(i):line_starts(i+1)-1)
END DO
end program nset_parser