ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.12.2023
Просмотров: 320
Скачиваний: 6
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
calls
— общее количество вызовов данной функции, т.е.
сколько раз данная функция была вызвана.
Если ни разу, или если невозможно определить
(например, если функция не была откомпилирована с опцией -pg), то поле calls – пусто;
self количество миллисекунд в среднем, затраченное ms/call данной функцией именно на её вызов (без учёта обращений к вызываемым ею функциям).
Если функция откомпилирована без опции -pg,
то данное поле пусто);
total количество миллисекунд в среднем, затраченное ms/call данной функцией и её подпрограммами на вызов
Если функция откомпилирована без опции -pg,
то данное поле пусто);
name
— имя функции. Список имён функций в самом правом столбце после упорядочения по убыванию по времени self seconds.
Подробнее узнать об остальных возможностях утилиты gprof мож- но посредством man gprof или info gprof, или же через Интернет, в частности,например, по ссылке:
http://www.opennet.ru/docs/RUS/gprof/gprof-6.html
372
— общее количество вызовов данной функции, т.е.
сколько раз данная функция была вызвана.
Если ни разу, или если невозможно определить
(например, если функция не была откомпилирована с опцией -pg), то поле calls – пусто;
self количество миллисекунд в среднем, затраченное ms/call данной функцией именно на её вызов (без учёта обращений к вызываемым ею функциям).
Если функция откомпилирована без опции -pg,
то данное поле пусто);
total количество миллисекунд в среднем, затраченное ms/call данной функцией и её подпрограммами на вызов
Если функция откомпилирована без опции -pg,
то данное поле пусто);
name
— имя функции. Список имён функций в самом правом столбце после упорядочения по убыванию по времени self seconds.
Подробнее узнать об остальных возможностях утилиты gprof мож- но посредством man gprof или info gprof, или же через Интернет, в частности,например, по ссылке:
http://www.opennet.ru/docs/RUS/gprof/gprof-6.html
372
13
Приложение IV. Операторы ввода-вывода
Операторы ввода-вывода ФОРТРАНа имеют вид (см. [2]):
• read(список_спецификаторов) [список_элементов_ввода]
• write(список_спецификаторов) [список_элементов_ввывода]
• read формат [,список_элементов_ввода]
• print формат [,список_элементов_вывода]
Две последние формы записи операторов ввода-вывода обычно использу- ются для выполнения операций ввода-вывода стандартными системны- ми устройствами, под которыми понимаются устройства ввода с экрана и вывода на экран, хотя ничто не мешает осуществить переназначение этих устройств посредством bash-операций < и >.
Напомним, что квадратные скобки в данном контексте не синтакси- ческий элемент записи оператора, а лишь указание на возможность от- сутствия элементов, помещённых в них. Например, вывод одного и того же текста, можно осуществить двояко:
write(*,*) ’Привет’
или write(*,’("Привет")’)
В первом случае оператор write имеет список элементов вывода (слово
Привет); во втором — это слово входит в список спецификаторов. Какой из способов выбрать — решает программист.
Список_спецификаторов предназначается для управления режи- мом работы операторов ввода-вывода. Спецификаторы могут задаваться либо в позиционной, либо в ключевой форме. Например,
program rw1; implicit none; integer n, m, k read(*,*) n
! позиционная форма устройства ввода и формата read(unit=*,fmt=*) m ! ключевая форма для устройства ввода и для формата read(fmt=*,unit=*) k ! ключевая форма: не важен порядок спецификаторов write(*,*) ’ n=’,n write(unit=*,fmt=*) ’ m=’,m; write(fmt=*,unit=*) ’ k=’,k end
373
Ключевые имена спецификаторов операторов ввода-вывода:
имя
Спецификатор
[unit=]
устройства ввода-вывода
[fmt=]
формата (только для форматных записей)
[nml=]
именованного списка rec=
номера записи (только для файла прямого доступа)
iostat=
типа ошибки err=
перехода по ошибке end=
перехода по признаку окончания файла advance= продвижения size=
числа введённых символов eor=
перехода по признаку окончания записи
13.1
Спецификатор [unit=]u u справа от знака равенства в unit=u при задании спецификатора в ключевой форме (или в позиционной — просто u) обозначает номер устройства, который можно задать:
• константой целого типа;
• выражением целого типа;
• именем внутреннего файла, в качестве которого используется пе- ременная символьного типа. Внутренний файл полезен, например,
когда возникает необходимость использовать в качестве повторите- ля оператора format имя упомянутой переменной (если, конечно, в арсенале средств оператора format нет соответствующей синтакси- чески встроенной возможности).
• символом * (звёздочка), который является синонимом стандартно- го устройства ввода-вывода, определяемого реализацией (обычно это экран). В gfortranе символу * по умолчанию сопоставляется номер 5 для устройства ввода и номер 6 для устройства вывода.
Узнать, какие именно номера сопоставляются устройствам ввода- вывода, обозначаемым cимволом * можно, иcпользуя встроенный в gfortran модуль ISO_FORTRAN_ENV, в котором номерам этих устройств сопоставлены именованные константы:
374
INPUT_UNIT и OUTPUT_UNIT
program main use ISO_FORTRAN_ENV
!
Результат работы:
implicit none
!
write(*,’(" INPUT_UNIT=",i3)’)
INPUT_UNIT
!
INPUT_UNIT=
5
write(*,’("OUTPUT_UNIT=",i3)’) OUTPUT_UNIT
!
OUTPUT_UNIT=
6
end program main
Программист, используя оператор read(*,*), полагает, что данные вводятся с экрана. Однако, если в программе устройство под но- мером 5 явно переопределено посредством оператора open на файл текущей директории, то и обращение read(*,*) потребует для ввода наличия соответствующего файла на диске. Например,
program test_unit; implicit none;
integer i, j read (*,*) i;
write(*,*) ’ i=’,i open(5,file=’input’ ); read (*,*) j; write(*,*) ’ j=’,j end
88
! файл input
$ gfortran main.f95
$ ./a.out
7
! Семёрка введена с экрана,
i=
7
! а 88 из файла с именем input j=
88
! несмотря на то, что оба ввода
./a.out > result
! выполнены оператором read(*,*)
5
i=
5
! файл result j=
88
Аналогично переопределяется и стандартный вывод при write(*,*), ес- ли в программе есть отработал оператор open(6,file=result).
375
13.2
Спецификатор формата [fmt=]f f справа от знака равенства в fmt=f при задании спецификатора в клю- чевой форме (или в позиционной — просто f) обозначает
• либо метку оператора format;
• либо имя массива (или выражение символьного типа), содержимое которых должно синтаксически подходить под описание формата.
• либо символ * (звёздочка), который в данном случае означает, что ввод-вывод управляется списком ввода-вывода.
program test_fmt; implicit none; integer i; character(5) s; logical b real r; complex c character(26) str /’(i4,4x,a5,4x,l7,6x,3f10.3)’/
character(4)
arr(7) /’(i4,’,’4x,a’,’5,4x’,’l7,6’,’x,3f’,’10.3’,’)
’/
read (*,fmt= 100) i, s, b, r, c; write(*,fmt= 1000) i, s, b, r, c read (*,fmt= str) i, s, b, r, c; write(*,1000) i, s, b, r, c read (*,fmt= arr) i, s, b, r, c; write(*,1000) i, s, b, r, c read (*,fmt=
* ) i, s, b, r, c; write(*,1000) i, s, b, r, c
100
format(i4,4x,a5,4x,l7,6x,3f10.3)
! 1000 format(’ i=’,i5,/’ s=’,a5,/’ b=’,l1/’ r=’,e11.4/,&
! &
’ c=(’,e10.3,’,’,e10.3,’)’)
1000 format(’ i=’,i5,3x,’s=’,a5,3x,’b=’,l1,3x,’r=’,e11.4,&
&
3x,’c=(’,e10.3,’,’,e10.3,’)’)
end
1234
abcde
.FALSE.
3.142 5.6
-9.5
файл input
5678
fghij
.true.
2.718 56.0e-01 -0.95e1 9876
klmno
.false.
1.111 56.0e-1
-0.95e+1 5432
pqrst
.TRUE.
4.444 (0.56,-9.5)
i= 1234
s=abcde b=F
r= 0.3142E+01
c=( 0.560E+01,-0.950E+01)
i= 5678
s=fghij b=T
r= 0.2718E+01
c=( 0.560E+01,-0.950E+01)
i= 9876
s=klmno b=F
r= 0.1111E+01
c=( 0.560E+01,-0.950E+01)
i= 5432
s=pqrst b=T
r= 0.4444E+01
c=( 0.560E+00,-0.950E+01)
376
13.3
Спецификатор именованного списка [nml=]q q справа от знака равенства в nml=q при задании спецификатора в ключевой форме (или в позиционной — просто q) обозначает имя списка объектов, заданное в операторе namelist.
program test_nml; implicit none; integer i; character(5) s; logical b real r; complex c namelist /my_list/ i, s, b, r, c; read (*,my_list)
write(*,my_list)
write(nml=my_list,unit=*)
end
&my_list b=.false., i=5555, c=(6.7,-9.9), r=0.333333, s=’uvwxy’
&end
&MY_LIST
! При отсутствии в операторах
I=
5555,
! ввода-вывода имени NML (ключе-
S="uvwxy",
! вого имени спецификатора име-
B=F,
! именованного списка) его
R= 0.333332986
,
! неключевое имя должно быть
C=(
6.69999981
, -9.89999962
), ! вторым в списке спецификаторов.
/
&MY_LIST
I=
5555,
S="uvwxy",
B=F,
R= 0.333332986
,
C=(
6.69999981
, -9.89999962
),
/
377
13.4
Спецификатор номера записи rec=n
Спецификатор используется в операторах ввода-вывода файлов только прямого доступа и только в ключевой форме. n справа от знака ра- венства в rec=n — номер записи в файле прямого доступа.
До сих пор и в лекционных программах, и в задачах домашних зада- ний мы имели дело только с файлами последовательного доступа (т.е.
прочесть нужную запись из такого файла можно было лишь посредством последовательного прохода всех предыдущих).
Доступ к записи файла прямого доступа происходит по её номе- ру, так что для чтения записи нет необходимости явно прочитывать все предшествующие.
Создадим текстовый файл c именем input1.dat, в каждой строке ко- торого поместим текст, содержащий, например, номер строки, точку и текст из четырёх символов:
1.abcd
! файл input1.dat
2.efgh
3.ijkl
4.mnop
5.qrst
Рассмотрим программу:
program t_rec_read; implicit none; integer i; character(5) s integer j open(8,file=’input1.dat’,access=’direct’,recl=7,form=’formatted’)
do j=1,5
read(8,rec=j,fmt=’(i1,a5)’) i, s; write(*,*) ’j=’,j,’ i=’,i, ’s=’,s enddo end
В ней файл input1.dat посредством оператора open открыт как файл прямого доступа:
• Оператор open(8,file=’input1.dat’,access=’direct’,recl=7,form=’formatted’)
подсоединяет файл с именем input1.dat к устройству под номе- ром 8. Для файлов последовательного доступа спецификатор access имеет значение ’sequential’ по умолчанию.
378
• access=’direct’ — устанавливаем режим прямого доступа к файлу.
• recl=7 — сообщаем, что длина записи состоит из семи байт (в пер- вый помещено однозначное целое, со второго по шестой помещены точка и четыре буквы). Когда, набрав очередную строку, нажима- ем enter, то заносим после самой правой последней буквы символ перевода строки (в ASCII десятичный код 10). Под этот одно- байтовый символ тоже требуется место. Поэтому recl=7. Если же указать recl=6, то
$ gfortran mainr.f95
$ ./a.out j=
1
i=
1 s= abcd
At line 7 of file mainr.f95 (unit = 8, file = ’input1.dat’)
Fortran runtime error: Bad value during integer read компиляция пройдёт успешно, но при запуске программы будет со- общено, что при чтении целого подаётся некорректное значение.
Причина: подача в качестве однобайтового целого признака пере- вода строки.
• form=’formatted’ — указываем, что form (спецификатор формат- ности) принимает значение ’formatted’ (форматный — форма со- держимого каждой записи файла определяется оператором format.
Настройка умолчания form зависит от режима доступа:
access=’direct’ — form=’unformatted’,
access=’sequental’ — form=’formatted’.
Далее программа просто читает записи из файла под именем input1.dat,
который через оператор open связан с устройством 8 в режиме прямо- го доступа при recl=7 и форматном способе чтения. Вывод результата чтения имеет вид j=
1
i=
1 s= abcd j=
2
i=
2 s= efgh j=
3
i=
3 s= ijkl j=
4
i=
4 s= mnop j=
5
i=
5 s= qrst
379
Пример записи файла прямого доступа program t_rec_write; implicit none; integer j, i, i0, i1;
character(5) s, s0, s1
open( 8,file=’input1.dat’,access=’direct’,recl=7,form=’formatted’)
open(10,file=’input2.dat’,access=’direct’,recl=7,form=’formatted’)
open(11,file=’input3.dat’,access=’direct’,recl=7,form=’formatted’)
do j=1,5
read(8,rec=j,fmt=’(i1,a5)’) i, s
! читаем input1.dat write(10,rec=j,fmt=’(i1,a5)’)i,s
! пишем в input2.dat write(11,rec=j,fmt=’(i1,a5,a1)’)i,s,transfer(10,’a’)! пишем в input3.dat enddo close(10); close(11)
open(10,file=’input2.dat’,access=’direct’,recl=7,form=’formatted’)
open(11,file=’input3.dat’,access=’direct’,recl=7,form=’formatted’)
do j=1,5
read(10,rec=j,fmt=’(i1,a5)’) i0, s0
! читаем input2.dat read(11,rec=j,fmt=’(i1,a5)’) i1, s1
! читаем input3.dat write(*,’(a,i3,a,a,i3,a)’) ’ 10: ’,i0, s0, ’
11:’,i1,s1
enddo end program t_rec_write
Программа t_rec_write считывает данные файла прямого доступа input1.dat (из предыдущего примера), и переписывает их в два других файла прямого доступа input2.dat и input3.dat:
1.abcd 2.efgh 3.ijkl 4.mnop 5.qrst
! файл input2.dat
1.abcd
! файл input3.dat
2.efgh
3.ijkl
4.mnop
5.qrst
Для того, чтобы каждая запись в файле прямого доступа начиналась с новой строки необходимо предыдущую запись завершать символом,
который соответствует нажатию клавиши enter. Символ при выводе в файл выглядит как пустое место (пробел), хотя таковым не является,
т.е. не виден. Получить именно его можно посредством вызова встроен- ной функции transfer(10,’a’), которая, не изменяя физического пред- ставления первого аргумента (в данном случае типа integer), позволяет посмотреть на него через очки типа char.
380
13.5
Спецификатор типа ошибки iostat=ios
Спецификатор используется только в ключевой форме. ios справа от знака равенства в iostat=ios (Input Output Status) есть имя скалярной переменной пользовательской программы для приёма кода причины за- вершения, который может оказаться следующим:
• 0 (нуль), если операция ввода-вывода завершилась успешно;
• >0 (целое большее нуля), если произошла ошибка;
• <0 (целое меньшее нуля): ошибок нет, но встречен признак окон- чания файла.
Конкретные числовые значения ios зависят от реализации.
Наряду с iostat имеются ещё и ключевые аргументы err и end, кото- рые позволяют передать управление на метку программы при возникно- вении одного из двух последних частных случаев iostat соответственно.
Обычно использование err и end выглядит проще, но требует постанов- ки метки и ссылки на неё, что при опечатке чревато неприятностями.
Ошибка, контролируемая iostat, может иметь разные обличья и воз- никать по разным причинам. Рассмотрим некоторые примеры.
Пусть программа должна вводить сначала значения целочисленной переменной n, затем двух вещественных переменных a и b, далее пе- ременной l булева типа, переменной c комплексного типа и, наконец,
символьной переменной w.
program test_iostat; implicit none; integer ios, n;
real a, b; logical l; complex c; character w read(*,*,iostat=ios) n, a, b, l, c, w; write(*,*) ’ ios=’,ios write(*,*) ’ n=’,n, ’ a=’,a, ’
b=’,b write(*,*) ’ l=’,l, ’
c=’,c, ’ w=’, w select case(ios)
case( 0 ); write(*,*) ’ главная ветвь обработки’; stop 0
case(:-1); write(*,*) ’ достигнут признак окончания вводимого файла’
stop 1
case( 1:); write(*,*) ’ ошибка ввода !!!’; stop 2
end select end
381
Пусть input — файл с правильными входными данными:
5 0.51 1.23
.true. (90.4,-35.3) ’M’
Тогда в результате пропуска программы test_iostat получим:
ios=
0
n=
5
a=
0.509999990
b=
1.23000002
l= T
c= (
90.4000015
, -35.2999992
)
w=M
главная ветвь обработки
Как и указывалось, значение ios получилось равным нулю и оператор select case выбрал соответствующую ветвь обработки.
Если при подготовке файла с входными данными случайно вместо целого значения первого вводимого параметра n указали вещественное,
то получили бы:
ios=
5010
n=
0
a=
0.00000000
b=
0.00000000
l= T
c= (
9397536.00
,
4.59163468E-41)
w=
ошибка ввода !!!
Как видно, ни одна из вводимых преременных не приняла вводимого значения.
Таким образом, ключевой аргумент iostat позволяет выбрать нуж- ную ветвь алгоритма, которая через посредство оператора stop может передать в переменную $? оболочки, код причины, по которой данный алгоритм завершил работу, что, в свою очередь, позволит оболочке адек- ватно выбрать соответствующую ветвь скрипта.
Использованием iostat c целью поиска ошибок ввода часто прене- брегают, так как без iostat=ios в качестве аргумента оператора read пропуск программы завершается аварийным сообщением вида:
At line 3 of file main.f95 (unit = 5, file = ’stdin’)
Fortran runtime error: Bad integer for item 1 in list input
Подобное сообщение содержит более полезную для поиска ошибки ин- формацию, чем фраза ошибка ввода, так как указывает и номер стро- ки исходного текста и даже номер и тип параметра, по вине которого произошёл аварийный останов.
382
Поэтому аргумент iostat=ios (в случае ios>0) необходим тогда, ко- гда при обнаружении ошибки выгодно не прекращать работу, а продол- жать, выводя программно, например, имена файлов и строки с бракован- ными данными для последующего их анализа, но продолжая обработку корректных.
Отрицательное значение фактического аргумента ios при iostat=ios может служить индикатором выхода из бесконечного цикла, если опера- тор чтения встречает признак окончания файла.
Так, достаточно типична ситуация, когда программа при разных про- пусках должна вводить разные файлы с неодинаковым количеством дан- ных. Вряд ли нас устроит ручной пересчёт этих количеств для того, что- бы указывать при очередном запуске программы: сколько данных она должна ввести.
Естественно, поручить подобный подсчёт самой программе при вводе.
Кстати, такой подсчёт может, в частности, служить и дополнитель- ным контролем ввода, если, всё-таки, заранее известно сколько строк должно быть введено.
Рассмотрим программу:
program test_iostat_1; implicit none; integer ios, k, m, n;
real a, b; logical l; complex c; character(1) q k=0; m=0
write(*,’(1x,"
n",7x,"a",9x,"b",6x,"l",5x,&
&
"Re(c)",7x,"Im(c)",4x,"q",6x,"ios" )’)
do k=k+1
read(*,*,iostat=ios) n, a, b, l, c, q select case(ios)
case( 0 ); m=m+1; call work case(:-1); write(*,*) ’ Число корректных строк ........ (m)=’,m; exit case( 1:); write(*,*) ’ Ошибка в строке под номером ... (k)=’,k end select enddo k=k-1
write(*,*) ’ Число вводимых строк k=’,k stop 0
contains subroutine work write(*,’(i4,f10.3,f10.3,l5,2(f10.3,3x),a,3x,i5)’)&
&
n, a, b, l, c, q, ios end subroutine work end
383