современный фортран , Бортеньев. О. В. Бартеньев Современный Фортран
Скачать 2.24 Mb.
|
9.1. Преобразование данных. Оператор FORMAT Перевод данных из внутреннего представления в текстовое задается дескрипторами преобразований (ДП). Так, для вывода вещественного числа на поле длиной в 8 символов, в котором 3 символа отведены для представления дробной части, используется дескриптор F8.3. Максимальное значение, которое можно отобразить на заданном поле, равно 9999.999, а минимальное - -999.999. Для преобразования внутреннего представления целого числа в текст длиной в 10 символов применяется дескриптор I10. Чтобы напечатать символьную переменную в поле длиной 25 знаков, применяется преобразование A25. 290 9. Форматный ввод/вывод Дескрипторы преобразования содержатся в спецификации формата, например: real :: a = -345.456 integer :: k = 32789 character(20) :: st = 'Строка вывода' write(*, '(1x, f8.3)') a !-345.456 write(*, '(1x, i10)') k ! 32789 write(*, '(1x, a25)') st ! Строка вывода Замечание. Символ использован для обозначения пробела. Спецификация формата включает заключенный в скобки список ДП. Спецификация может быть задана как встроенная в оператор В/В символьная строка, например: '(F8.3, I10)' или как отдельный оператор FORMAT, на который операторы В/В ссылаются при помощи метки. Общий вид оператора: метка FORMAT (список ДП) ДП разделяются в списке ДП запятыми. Например: write(*, '(1x, f8.3, i10)') a, k !-345.456 32789 write(*, 1) a, k !-345.456 32789 1 format(1x, f8.3, i10) В каждой из приведенных спецификаций формата содержится ДП 1X. При форматном выводе его присутствие необходимо, правда, только в FPD. Дело в том, что в FPS при форматном выводе по умолчанию первая позиция строки вывода предназначена для простановки символа управления кареткой печатающего устройства. Возможные символы управления кареткой приведены в табл. 9.1. Таблица 9.1. Символы управления кареткой печатающего устройства Символ Действие Пробел Начать новую строку + Остаться на той же строке (перепечатать) 0 Пропустить одну строку 1 Перейти на начало следующей страницы Поэтому первая позиция строки вывода на экране и печатающем устройстве не отображается. Чтобы исключить генерацию ложных символов управления кареткой в FPS существует два средства: 291 О. В. Бартеньев. Современный ФОРТРАН • можно всегда вставлять при форматном выводе по крайней мере один пробел в качестве первого символа в каждую запись. Это выполняется дескриптором 1X или T2; • можно подсоединить внешнее устройство, задав в операторе OPEN спецификатор CARRIAGECONTROL = 'LIST' (разд. 11.4). В этом случае первый символ каждой записи при форматном выводе не будет интерпретироваться как символ управления кареткой и будет выводиться на внешнем устройстве, например: write(*, 1) 'abcd' ! bcd open(6, carriagecontrol = 'list') ! по умолчанию устройство 6 - это экран монитора write(*, 1) 'abcd' ! abcd 1 format(a) В CVF по умолчанию CARRIAGECONTROL = 'LIST', поэтому предварять список ДП дескриптором 1X, если правила умолчания не изменены, нет необходимости. Пример: write(*, '(i3)') 123 ! CVF: 123 end ! FPS: 23 Замечание. В DS есть возможность редактирования оператора FORMAT. Для этого установите курсор на оператор FORMAT, в котором есть хотя бы один ДП, и затем выполните цепочку Edit - Fortran Format Editor. 9.2. Программирование спецификации формата Спецификацией формата является символьная строка. Наиболее часто значение этой строки задается в виде буквальной символьной константы так, как это было выполнено в примерах предыдущего раздела. Однако в общем случае спецификацией формата может быть и символьная переменная, значение которой может изменяться в процессе вычислений. Пример. Запрограммировать формат для вывода заголовка по центру экрана. Задачу решить, предполагая, что длина заголовка меньше ширины экрана. Введем обозначения: tl - длина заголовка без завершающих пробелов; sl - ширина экрана (в текстовом режиме ширина экрана составляет 80 символов). Для центрирования заголовка необходимо отступить от левой границы экрана n = (sl - tl)/2 символов, а затем вывести заголовок. Так, при tl = 60 следует применить формат '(11X, A)' или '(T12, A)' а при выводе заголовка длиной в 40 символов подошел бы формат '(21X, A)' или '(T22, A)' 292 9. Форматный ввод/вывод Текст программы формирования формата вывода заголовка длиной tl: program t2 character(78) :: title = 'Пример заголовка' character(20) form ! Строка формата вывода integer(1) tl, n, sl /80/ tl = len_trim(title) ! Длина заголовка без завершающих пробелов n = (sl - tl) / 2 ! Формирование формата вывода (строки fmt) с дескриптором X write(form, '(a, i2, a)') '(', n, 'x' // ',' // 'a' // ')' ! или в случае использования дескриптора T ! write(form, '(a, i2, a)') '(' // 't', n + 1, ',' // 'a' // ')' write(*, form) title ! Вывод заголовка end program t2 Пояснения: 1. Строка является внутренним файлом, при работе с которым используется форматный В/В. 2. Дескриптор Tn смещает позицию В/В на n символов вправо. Замечания: 1. Задание формата '(nX, A)' или '(Tn, A)' является ошибкой, так как в этом случае дескриптор содержит недопустимый для формата символ n, вместо которого должна быть использована буквальная положительная целая константа без знака. Далее, правда, мы покажем, что такие целые константы могут быть заменены заключенным в угловые скобки целочисленным выражением (разд. 9.3). 2. На самом деле при выводе в DOS-окно заголовок, содержащий русский текст, будет выведен как нечитаемый набор символов. Чтобы поправить положение, необходимо использовать приведенную в прил. 1 функцию Ru- DosWin, принадлежащую модулю TextTransfer, и внести в приведенный выше код следующие изменения: module TextTransfer ! Код модуля TextTransfer см. в прил. 1 end module TextTransfer program t2 use TextTransfer ! Для вывода русского текста character(78) :: title = 'Пример заголовка' ... ! Код программы t2 см. выше write(*, form) trim(RuDosWin(title, .false.)) ! Вывод заголовка end program t2 Далее, впрочем, как и ранее, ссылка на модуль TextTransfer будет опускаться, но всегда, если выводится русский текст, будет подразумеваться, так же как и соответствующее употребление RuDosWin. 293 О. В. Бартеньев. Современный ФОРТРАН Формат также может быть задан в виде символьного массива, элементы которого при выполнении В/В конкатенируются. Пример. Запрограммировать формат вывода заголовка с применением символьного массива. character(78) :: title = 'Пример заголовка' character(1) fmt(12)/'(', '1', 'x', ',', 't', 2*' ', ',', 'a', ')', 2*' '/ integer(1) n, sl /80/ n = (sl - len_trim(title))/2 select case(n) case(1:9) ! Преобразуем n в символьное write(fmt(6), '(i1)') n ! представление и занесем в fmt(6) case(10:) ! или в fmt(6) и fmt(7), если n > 9 write(fmt(6), '(i1)') n/10 write(fmt(7), '(i1)') mod(n, 10) endselect write(*, fmt) title Символы строки или элементы массива, расположенные после крайней правой скобки строки - спецификации формата, игнорируются. Поэтому и строка и массив могут содержать большее число элементов, чем необходимо для задания формата. При программировании формата надо помнить, что спецификация формата должна быть полностью установлена перед началом выполнения оператора В/В. Во время исполнения оператора В/В ни один символ спецификации формата не может быть изменен. 9.3. Выражения в дескрипторах преобразований Если в строке формата дескриптор преобразований использует целочисленную константу, то она может быть заменена заключенным в угловые скобки (< >) целочисленным выражением: integer :: m, k k = 10 do m = 3, 5 k = k*10 write(*, '(2x, i ! 100 end do ! 1000 end ! 10000 Целочисленное выражение может быть любым допускаемым выражением со следующими ограничениями: • в дескрипторе H константа не может быть заменена целочисленным выражением; • операции отношения в этом выражении не могут быть заданы в графическом виде, например вместо знака > используется .GT., вместо <= - .LE.. 294 9. Форматный ввод/вывод Задаваемое вместо константы целочисленное выражение не может появляться в операторе присваивания при программировании строки формат; так, ошибочен фрагмент: integer :: m = 2, k = 5 character(80) s s = '(2x, i ! Ошибка Но корректны операторы: integer :: m = 2, k = 5 write(*, '(2x, i ! 2 5 print 1, m, k ! 2 5 1 format( Заменяя в ДП константу на выражение, нужно следить за тем, чтобы выражение было целочисленным и возвращаемое им значение было больше нуля. Пример. Вывести заголовок по центру экрана. character(78) :: title = 'Пример заголовка' integer(1) :: tl, sl = 80 ! sl - ширина экрана tl = len_trim(title) write(*, fmt = 10) title ! Вывод заголовка 10 format(< (sl - tl)/2 > x, a) 9.4. Задание формата в операторах ввода/вывода При форматном В/В операторы В/В содержат ссылку на используемый формат. Такая ссылка может быть задана четырьмя способами: • в виде метки, указывающей на оператор формата: write(*, 10) a, k или write(*, fmt = 10) a, k 10 format(1x, f8.3, i10) Замечание. Метка в случае ее использования для ссылки на формат может быть присвоена целочисленной переменной оператором ASSIGN (прил. 4), который, правда, удален из Фортрана стандартом 1995 г.: integer :: label, m = 55 assign 20 to label print label, m ! 55 20 format(1x, i5) • в виде встроенного в оператор В/В символьного выражения: write(*, '(1x, f8.3, i10)') a, k 295 О. В. Бартеньев. Современный ФОРТРАН или write(*, fmt = '(1x, f8.3, i10)') a, k • в виде имени именованного списка В/В: integer :: k = 100, iarray(3) = (/ 41, 42, 43 /) real :: r4*4 = 24.0, r8*8 = 28.0 namelist /mesh/ k, r4, r8, iarray write(*, mesh) или write(*, nml = mesh) • в виде звездочки, указывающей на использование управляемого неименованным списком В/В: write(*, *) a, k write(*, fmt = *) a, k 9.5. Списки ввода/вывода Оператор ввода для каждого элемента списка ввода находит во внешнем файле поле с данными и читает из него в элемент списка значение. Оператор вывода создает в файле по одному полю с данными для каждого элемента списка вывода. В случае форматного вывода размер поля определяется примененным форматом. 9.5.1. Элементы списков ввода/вывода Элементами списка В/В могут быть как полные объекты данных любых типов (скаляры и массивы), так и их подобъекты, например компоненты записи, элементы массива, сечение массива, подстрока. Между списками ввода и вывода есть различия: список ввода может содержать только переменные и их подобъекты, список вывода содержит выражения. Пример: type point real x, y character(8) st end type point type(point) p(20), pxy open(1, file = 'a.txt') read(1, '(2f8.2)') pxy.x, pxy.y ! Возможные списки ввода read(1, '(f8.2 / f8.2 / a)') p(1).x, p(1).y, p(1).st read(1, 20) pxy ! В списке ввода 3 элемента read(1, 20) (p(k), k = 1, 20) ! В списке ввода 60 элементов 20 format(2f8.2, a) Присутствующий в списке В/В скалярный объект встроенного типа (кроме комплексного) создает один элемент В/В. Массив встроенного типа (кроме комплексного) добавляет в список В/В все свои элементы. Порядок 296 9. Форматный ввод/вывод следования элементов массива в списке В/В совпадает с порядком их расположения в памяти ЭВМ. Так, эквивалентны списки: real a(2, 3) / 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 / write(*, *) a ! В списке вывода 6 элементов write(*, *) a(1, 1), a(2, 1), a(1, 2), a(2, 2), a(1, 3), a(2, 3) Скаляр комплексного типа создает два элемента В/В. В случае комплексного массива из n элементов в список В/В добавляется 2*n элементов. Компоненты скаляра производного типа располагаются в списке В/В в том же порядке, в котором они располагаются и в операторе объявления этого типа. В случае форматного ввода число присутствующих во внешнем файле полей ввода должно быть не меньше числа элементов в списке В/В. Размер занимаемого вводимой величиной поля и его положение в файле должны быть согласованы с форматом ввода. Например: integer(2) k, m, a(20), b(10) complex(4) z character(30) art(15) character(30) :: fmt = '(4i4 / 10i3 / 2f8.2 / (a30))' open(1, file = 'a.txt') read(1, fmt) k, m, a(2), a(4), b, z, art Список ввода содержит 31 элемент: 25 из них дают массивы b и art, 2 - комплексная переменная z и по одному переменные k, m, a(2), a(4). Следовательно, не менее 31 значения должно присутствовать и в файле, из которого выполняется ввод данных. Число строк в файле не может быть менее 18, поскольку в спецификации формата fmt присутствует преобразование слеша (/), которое обеспечивает перемещение файлового указателя на начало новой записи. Положение и состав полей файла может быть, например таким (символ использован для обозначения пробела): 111 222 333 444 11 12 13 14 15 16 17 18 19 20 1111.11 2222.22 строка 1 строка 15 При составлении списка В/В следует учитывать ограничения: • в списке В/В не может появляться перенимающий размер массив, но могут появляться его подобъекты (элементы и сечения); • присутствующий в списке В/В размещаемый массив должен быть к моменту выполнения В/В размещен; • все ссылки списка В/В к моменту выполнения В/В должны быть прикреплены к адресатам. Передача данных выполняется между файлом и адресатом; 297 О. В. Бартеньев. Современный ФОРТРАН • каждый конечный компонент присутствующего в списке В/В объекта производного типа не должен иметь атрибут PRIVATE; • в списке В/В не могут присутствовать объекты производного типа, у которых среди компонентов какого-либо уровня есть ссылки. Список В/В может быть пустым. Тогда при выводе создается запись нулевой длины. При вводе выполняется переход к следующей записи. Если же при пустом списке вывода используется формат, состоящий только из строки, то будет выведена запись, содержащая эту строку, например: write(*, '(1x, "I am a test string")') 9.5.2. Циклические списки ввода/вывода Список В/В может также содержать и циклический список, имеющий вид: (список объектов цикла, dovar = start, stop [, inc]) где каждый объект цикла - это переменная (в случае ввода), или выражение (в случае вывода), или новый циклический список; dovar - переменная цикла - целая скалярная переменная; start, stop, inc - целые скалярные выражения. Циклический список оператора В/В работает так же, как и DO- цикл с параметром или неявный цикл оператора DATA и конструктора массива. Другое название циклического списка оператора В/В - неявный цикл оператора В/В. Пример. Вывод горизонтальной линии. print '(1x, 80a1)', ('_', k = 1, 80) ! В списке вывода 80 элементов 9.5.3. Пример организации вывода Задача: выполнить табуляцию функции двух переменных: z = | x - y | e y/3 /(1/3 + cos(x/y)) при изменении x от 1 до 5 с шагом 0.5, а y - от 1.1 до 1.5 с шагом 0.05. Оформим результат в виде таблицы, содержащей заголовок, значения x по вертикали и значения y по горизонтали. В ячейках таблицы выведем соответствующие аргументам x и y значения z (рис. 9.1). Зависимость z = ABS(x - y) * EXP(y/3)/(1/3 + cos(x/y)) x \ y 1.10 1.15 1.20 ... 1.50 1.00 z 1,1 z 1,2 z 1,3 z 1,9 z 2,1 z 2,2 z 2,3 z 2,9 1.50 z 11,1 z 11,2 z 11,3 z 11,9 5.00 Рис. 9.1. Проект таблицы вывода (выходная форма ) 298 9. Форматный ввод/вывод Ясно, что для организации такой таблицы потребуется выполнить некоторые преобразования: смещение позиции вывода, форматирование вывода значений x, y и z, вывод символьных данных. Для вывода нам дополнительно надо знать: • допустимое число выводимых на одной строке символов (в случае консоль-проекта это число равно 80); • диапазон изменения значений функции z; • необходимую точность представления z (число десятичных знаков). Максимальное и минимальное значения z, а также точность представления z нужны для определения, во-первых, длины необходимого для вывода z поля и, во-вторых, способа представления z (в F- или Е- форме). В общем случае эти данные могут быть определены лишь в процессе вычислений. Рассмотрим подробно механизм формирования формата вывода одной строки таблицы. Положим, что максимальное и минимальное значения z могут быть размещены на поле длиной в 7 символов, причем два правых символа поля будут расположены после десятичной точки. Такое поле задается преобразованием F7.2. Расстояние между полями вывода z положим равным единице. Тогда при выводе одного поля следует использовать формат 1X, F7.2. Всего в одной строке таблицы будет размещено 9 полей со значениями z. Для их вывода необходим формат 9(1X, F7.2). Теперь предусмотрим при выводе строки значений z отступ от левой границы экрана в 1 символ и последующий вывод значения x в поле длиной в 5 символов, содержащее два десятичных знака. Получаем формат вывода строки таблицы: (2X, F5.2, 9(1X, F7.2)). program zxy real :: x, y, xa = 1.0, xb = 5.0, ya = 1.1, yb = 1.51 real :: z(10) ! Массив значений z для строки таблицы real :: dx = 0.5, dy = 0.05 ! Шаг изменения x и y character(80) :: title = 'Зависимость z = ABS(x - y) * EXP(y/3)/(1/3 + cos(x/y))' integer(1) k, tab tab = (80 - len_trim(title)) / 2 write(*, '( ! Вывод заголовка по центру экрана write(*, 1) ('_', k = 1, 80) ! Вывод горизонтальной линии write(*, '(2x, a, 9f8.2)') 'x \ y', (y, y = ya, yb, dy) write(*, 1) ('_', k = 1, 80) ! Вновь выводим горизонтальную линию x = xa do while(x <= xb) k = 0 y = ya do while(y <= yb) ! Формирование массива значений z k = k + 1 z(k) = abs(x - y) * exp(y / 3.0) / (1.0/3.0 + cos(x / y)) y = y + dy |