В Fortran 90, каков хороший способ записи массива в текстовый файл, по строкам?
Я новичок в Fortran, и я хотел бы иметь возможность писать двумерный массив в текстовый файл, по строкам (пробелы между столбцами и каждая строка в своей собственной строке). Я пробовал следующее, И, похоже, работает в следующем простом примере:
PROGRAM test3
IMPLICIT NONE
INTEGER :: i, j, k, numrows, numcols
INTEGER, DIMENSION(:,:), ALLOCATABLE :: a
numrows=5001
numcols=762
ALLOCATE(a(numrows,numcols))
k=1
DO i=1,SIZE(a,1)
DO j=1,SIZE(a,2)
a(i,j)=k
k=k+1
END DO
END DO
OPEN(UNIT=12, FILE="aoutput.txt", ACTION="write", STATUS="replace")
DO i=1,numrows
WRITE(12,*) (a(i,j), j=1,numcols)
END DO
END PROGRAM test3
Как я уже сказал, Это, кажется, работает нормально в этом простой пример: полученный текстовый файл aoutput.txt, содержит номера 1-762 на линии 1, номера 763-1524 на линии 2, и так на.
строка мудрый запись в текстовый файл рутина, которую я должен использовать?большое спасибо за ваше время. Я действительно ценю это.
3 ответов
здесь есть несколько проблем.
фундаментальный, что вы не должны использовать текст в качестве формата данных для значительной части данных. Он большой и медленный. Текстовый вывод хорош для того, что вы собираетесь прочитать сами; вы не собираетесь сидеть с распечаткой 3,81 миллиона целых чисел и листать их. Как показано в приведенном ниже коде, правильный вывод текста примерно в 10 раз медленнее и на 50% больше, чем двоичный вывод. При переходе к значениям с плавающей запятой, существуют проблемы потери точности с использованием строк ascii в качестве формата обмена данными. так далее.
если ваша цель-обмен данными с matlab, довольно легко записать данные в формат, который может читать matlab; вы можете использовать API matOpen/matPutVariable из matlab или просто записать его как массив HDF5, который может читать matlab. Или вы можете просто записать массив в RAW Fortran binary, как показано ниже, и иметь matlab прочитал его.
если вы должны использовать ascii для напишите огромные массивы (что, как уже упоминалось, является плохой и медленной идеей), тогда вы столкнетесь с проблемами с длиной записи по умолчанию в списке-drected IO. Лучше всего генерировать во время выполнения строку формата, которая правильно описывает ваш вывод, и безопаснее всего для такого большого (~5000 символов!) lines-установить длину записи явно на что-то большее, чем то, что вы будете распечатывать, чтобы библиотека Fortran IO не помогала вам разбивать строки.
в коде ниже,
WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))'
генерирует строку rowfmt, которая в этом случае будет (762(1X,I6))
какой формат вы будете использовать для печати, и до OPEN
устанавливает длину записи как нечто большее, чем 7 * numcols + 1.
PROGRAM test3
IMPLICIT NONE
INTEGER :: i, j, k, numrows, numcols
INTEGER, DIMENSION(:,:), ALLOCATABLE :: a
CHARACTER(LEN=30) :: rowfmt
INTEGER :: txtclock, binclock
REAL :: txttime, bintime
numrows=5001
numcols=762
ALLOCATE(a(numrows,numcols))
k=1
DO i=1,SIZE(a,1)
DO j=1,SIZE(a,2)
a(i,j)=k
k=k+1
END DO
END DO
CALL tick(txtclock)
WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))'
OPEN(UNIT=12, FILE="aoutput.txt", ACTION="write", STATUS="replace", &
RECL=(7*numcols+10))
DO i=1,numrows
WRITE(12,FMT=rowfmt) (a(i,j), j=1,numcols)
END DO
CLOSE(UNIT=12)
txttime = tock(txtclock)
CALL tick(binclock)
OPEN(UNIT=13, FILE="boutput.dat", ACTION="write", STATUS="replace", &
FORM="unformatted")
WRITE(13) a
CLOSE(UNIT=13)
bintime = tock(binclock)
PRINT *, 'ASCII time = ', txttime
PRINT *, 'Binary time = ', bintime
CONTAINS
SUBROUTINE tick(t)
INTEGER, INTENT(OUT) :: t
CALL system_clock(t)
END SUBROUTINE tick
! returns time in seconds from now to time described by t
REAL FUNCTION tock(t)
INTEGER, INTENT(IN) :: t
INTEGER :: now, clock_rate
call system_clock(now,clock_rate)
tock = real(now - t)/real(clock_rate)
END FUNCTION tock
END PROGRAM test3
Это может быть очень окольный и трудоемкий способ сделать это, но все равно... Вы можете просто распечатать каждый элемент массива отдельно, используя advance='no'
(чтобы подавить вставку символа новой строки после того, что печаталось) в вашем write
заявление. Как только вы закончите с линией, вы используете "нормальный"write
утверждение, чтобы получить символ новой строки, и начать снова на следующей строке. Вот небольшой пример:
program testing
implicit none
integer :: i, j, k
k = 1
do i=1,4
do j=1,10
write(*, '(I2,X)', advance='no') k
k = k + 1
end do
write(*, *) '' ! this gives you the line break
end do
end program testing
при запуске этой программы выводится следует:
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 33 34 35 36 37 38 39 40
использование " * " направлено на список IO -- Fortran будет принимать решения за вас. Некоторые типы поведения не указаны. Вы можете получить больше контроля, используя оператор format. Если вы хотите положительно определить границы строк, вы пишете символ маркера после каждой строки. Что-то вроде:
DO i=1,numrows
WRITE(12,*) a(i,:)
write (12, '("X")' )
END DO
добавление несколько часов спустя:
возможно, при больших значениях numcols строки слишком длинны для некоторых программ, которые вы используете для изучения файла? Для выведите оператор, попробуйте:
WRITE(12, '( 10(2X, I11) )' ) a(i,:)
который разбивает каждую строку матрицы, если она имеет более 10 столбцов, на несколько более коротких строк в файле.