Programmieren in Fortran 90/95


  1. Formatbeschreiber
  2. Nicht immer genügt das listengesteuerte Ausgabeformat
       write(*,*) <Variablenliste>   
    
    - das Sternchen an der 2. Stelle steht für die Formatangabe "listengesteuert" bzw. "systembestimmt" der nachfolgenden Variablenwerte - den Anforderungen des Anwenders nach einem angemessenen Ausgabeformat. Zum Beispiel sollen Geldbeträge in Euro und Cent Regel mit 2 Nachkommastellen dargestellt werden. So soll z.B. eine Preisangabe von 9.98 Euro auf dem Bildschirm als 9.98 und nicht als 9.980000 erscheinen.

    Einfaches Beispiel:

program euro
  implicit none
  real :: preis = 9.98
  write(*,*) 'listengesteuertes Ausgabeformat:'
  write(*,*) preis
  write(*,*)
  write(*,*) 'formatgesteuertes Ausgabeformat:'
  write(*,'(F8.2)') preis
end program euro

Führt zu der Bildschirmausgabe
 listengesteuertes Ausgabeformat:
   9.980000    
 
 formatgesteuertes Ausgabeformat:
    9.98

Bei der formatgesteuerten Ausgabe wurde das zweite Sternchen durch die Formatangabe '(F8.2)' ersetzt. Dadurch wird der Wert der real-Variablen preis mit zwei Nachkommastellen in einem insgesamt 8 Stellen breiten Feld rechtsbündig ausgegeben. Vor der Zahl 9.98 befinden sich somit noch 4 Leerstellen.

Durch die Formatbeschreiber lassen sich in Fortran die Ausgabeformate den Erfordernissen des Anwenders anpassen.

Die drei Möglichkeiten, Formate festzulegen

Es sei vereinbart:
   integer :: i = 123456
   real    :: x = 3.141593
Möglichkeit 1: direkt die Formatbeschreiberkette angeben:
   write(*,'(1X,I6,F10.2)') i, x
Möglichkeit 2: die "Anweisungsnummer" (engl. statement label ) einer zugehörigen format-Anweisung angeben:
   write(*,100) i, x
   100 format(1X,I6,F10.2) 
Möglichkeit 3: den Namen einer Zeichenkette, die den Formatbeschreiber enthält, als Formatangabe eintragen:
   character(len=16) :: string

   string = '(1X,I6,F10.2)'
   write(*,string) i, x

Beispiel: format_beispiel.f90
program format_beispiel
   implicit none
   integer   :: i = 123456
   real      :: x = 3.141592

   character(len=16) :: string = '(1X,I6,F10.2)'   ! Formatangabe

! Vorstellung der 3 Moeglichkeiten, Formatbeschreiber anzugeben

     ! 1. Moeglichkeit: direkte Angabe der Formatbeschreiberkette
       write(*,'(1X,I6,F10.2)') i, x

     ! 2. Moeglichkeit: Angabe einer Anweisungsnummer, die die zugehoerige
     !                  Format-Anweisung enthaelt
       write(*,100) i, x
100    format(1X,I6,F10.2) 

     ! 3. Moeglichkeit: Angabe einer Zeichenkette, die die 
     !                   Formatbeschreiberkette enthaelt
       write(*,string) i, x
        
end program format_beispiel

Bildschirmausgabe: format_beispiel.erg
 123456      3.14
 123456      3.14
 123456      3.14

Jede der 3 angegebenen Möglichkeiten, nacheinander den Wert von i und x auszugeben, ist in der Wirkung gleich:
Die Formatangabe '(1X,I6,F10.2') bewirkt in der write-Anweisung, dass bei der Ausgabe der Werte von i und x zunächst eine Leerstelle ausgegeben wird (1X), die folgenden 6 Felder Breite werden zur Ausgabe des Wertes von i verwendet. Da die Zahl 123456 genau 6 Stellen breit ist, werden diese 6 Felder vollständig von der Zahl ausgefüllt. Die folgenden 10 Felder sind zur Ausgabe der real-Zahl, der der Variablen x zugewiesen wurde, vorgesehen. Die Ausgabefelder werden prinzipiell von rechts her aufgefüllt. F ist einer der Möglichkeiten, Werte vom Datentyp real formatiert ausgeben zu lassen. Durch F wird festgelegt, dass es sich um eine Festpunktzahl handeln soll, deren Anzahl der Nachkommastellen im obigen Beispiel durch die Zahl hinter dem . (Punkt) im Formatbeschreiber, und somit auf 2 Nachkommastellen festgelegt wurde. An der 3. Stelle von rechts seht dann der Dezimalpunkt, an der 4. Stelle steht dann die 3. Deshalb bleiben zwischen der letzten Ziffer ausgegebenen Ziffer von i und der ersten ausgegebenen Ziffer von x 6 Leerzeichen (Blanks) frei.

Allgemeine Bemerkungen zu den 3 Möglichkeiten der Formatangabe


Möglichkeit 1 (die direkte Angabe der Formatbescheiberkette) ist sinnvoll, wenn die Formatangabe nur einmal benötigt wird.

Möglichkeit 2 bietet sich an, wenn derselbe Formatbeschreiber an mehreren Stellen der gleichen Programmeinheit Verwendung finden kann. format-Anweisungen können an beliebiger Stelle im Anweisungsteil einer Programmeinheit stehen. Es bietet sich an, sie an exponierter Stelle (z.B. unterhalb der Variablendeklarationen) einzufügen, so dass sie leicht angepasst werden können. Bei der format-Anweisung handelt es sich um eine nicht ausführbare Anweisung. An das entsprechende statement label kann somit nicht mittels goto gesprungen werden.

Möglichkeit 3 bietet den Vorteil, sich mittels Zeichenkettenverarbeitung den Formatbeschreiber innerhalb des Programms den Erfordernissen des Ausgabeformats geeignet anzupassen. (z.B. die universelle Ausgabe von n x n Matrizen, wobei n zwischen 2 und 10 liegen kann) in einem gut lesbaren Format.

Generelles zu den Formatbeschreiberketten

Die einzelnen Formatangaben in der Formatbeschreiberkette werden in der Regel durch Kommata voneinander getrennt.

Ausnahme: bei / für den Zeilenvorschub (new line) ist dies nicht notwendig, ebensowenig bei $ zur Unterdrückung des Zeilenvorschubs am Ende einer write-Anweisung.

Formatbeschreiberketten lassen sich in ein Klammerpaar einschliessen und vor dieses lässt sich wiederum eine Zahl als Wiederholungfaktor stellen. Zum Beispiel wären

 
    10 format(3(1X,F12.2))
und
 
    20 format(1X,F12.2,1X,F12.2,1X,F12.2)
in ihrer Wirkung identisch.

Es ist auch möglich, in die Formatangabe Textbestandteile einzubauen. Soll z.B. eine Zeilennummer und ein Wert hintereinander mit einem Kommentar ausgeben werden, so könnte man z.B. schreiben

       write(*,30) i, x 
       30 format(1X,'Zeilennummer =',I7,','2X,'Wert =',F12.6)
ergibt als Output
        Zeilennummer = 123456,  Wert =    3.141593

Allgemeine Bemerkungen zum Umgang mit Formatbeschreibern

Falls Sie Formatbeschreiber einsetzen, bitte stets auf die Übereinstimmung von Formatbeschreiber und Datentyp der Variaben achten! Ein falscher Formatbeschreiber kann unter Umständen zu run time errors (Laufzeitfehlern) führen oder zur Ausgabe einer Sequenz an Sternchen.

Durch die Angabe eines Formatbeschreiber kann man die Genauigkeit in der Zahlendarstellung für den Datentyp real reduzieren, z.B. werden mit F11.3 nur noch 3 signifikante Nachkommastellen und mit E11.3 nur noch insgesamt 3 signifikante Stellen einer Zahl dargestellt. Dabei rundet Fortran mathematisch korrekt auf oder ab.

Generell ist beim Umgang mit Formatbeschreibern darauf zu achten, dass es niemals ungewollt zu einem Verlust an Genauigkeit kommen darf. Sollen z.B. numerisch gewonnnene Daten auf Dateien geschrieben und später weiterverarbeitet werden, so ist z.B. fast immer die Ausgabe im listengesteuerten Format sinnvoll, weil dadurch die maximale Darstellungsgenauigkeit der Zahl erhalten bleibt.

Im Einzelfall kann es allerdings auch Sinn machen, die Darstellungsgenauigkeit der Zahlen bei der Abspeicherung anzupassen, beispielsweise, wenn es sich um Messwerte mit einer vorgegeben Genauigkeit handelt. Liegen z.B. Messwerte mit 3 Stellen Genauigkeit vor, so würde es keinen Sinn machen, die Zahlen listengesteuert auf 7 Stellen genau abzuspeichern. In diesem Fall würde nur unnötig Speicherplatz verbraucht werden.

Formatbeschreiber für den Datentyp integer

Dieser kann z.B. lauten
  • Iw oder rIw oder rIw.m.
Hier und im folgenden bedeuten
  • w = Feldbreite
  • r = Wiederholungsfaktor
  • m = Mindestanzahl der darzustellenden Zeichen

Im Beispiel oben wurde vereinbart

integer   :: i = 123456
Damit hat i einen sechstelligen Wert erhalten. Mit dem Formatbeschreiber '(I6)' wird vereinbart, dass für die Ausgabe von i 6 Stellen vorgesehen werden. In diesem Beispiel stimmen Feldbreite und Anzahl der Ziffern überein. Mit der Anweisung
write(*,'(I6)') i
wird der Wert von i auf dem Bildschirm rechtsbündig in ein Feld mit 6 Stellen Breite geschrieben als
123456
Die Ausgabe beginnt also direkt am linken Rand. Durch die Anweisung
write(*,'(I8)') i
wird die Feldbreite auf 8 Stellen erhöht. Der Wert von i (im Beispiel ist dies 123456 wird rechtsbündig in das 8 Stellen breite Feld eingefügt und damit beginnt die Bildschirmausgabe mit 2 Leerstellen
  123456
Die Zahlen werden rechtsbündig in die angegebene Ausgabefeldbreite eingefügt. Solange w grösser ist als die Anzahl der Ziffern l, werden w-l Leerzeichen der Zahl vorangestellt. Müssen mehr Ziffern ausgegeben werden, als der Wert von w angibt, passt die Zahl nicht mehr in die dafür vorgesehene Feldbreite. In diesem Fall werden w Sternchen "gedruckt".
Konkret: würde man im obigen Beispiel
write(*,'(I5)') i

verlangen, dass die sechstellige Ziffer 1234567 in ein Feld der Breite 5 ausgeben wird, so kann dies nicht funktionieren. Die Fortran-Compiler verhalten sich nach dem Standard so, dass in diesem Fall 5 (im allgemeinen Fall w) Sternchen ausgeben werden

*****
so dass, wenn Werte nicht in das programmierte Ausgabeformat passen, der Wert der Feldbreite w als Sternchen auf dem Bildschirm erscheinen. In diesem Fall ist der Programmierer aufgefordert, diesen Fehler im Ausgabeformat zu beheben.

Beispielprogramm:
format_integer.f90, Bildschirmausgabe: format_integer.erg

Wird eine Mindestbreite m im integer-Formatbeschreiber vorgeben, und sei die Zahl insgesamt l Ziffern lang, so werden insgesamt m-l Nullen der Zahl vorangestellt. Dies könnte man nutzen wenn man eine Art Zähler programmieren will.

write(*,'(I8.7)') i

bedeutet, dass der Wert von i rechtbündig in ein Feld der Breite 8 und mit mindestens 7 Stellen ausgeben werden soll. Da im obigen Beispiel i nur 6 Stellen besitzt (l=6) werden der Zahl m-l = 7-6 = 1 Null vorangestellt. Vor der ausgegebenen Zahl befindet sich aufgrund der gewählten Beispielzahlen natürlich noch eine Leerstelle.

 0123456
Beispiel: format_integer_feldbreite.f90, Bildschirmausgabe: format_integer_feldbreite.erg

Binär-, Octal- und Hexadezimaldarstellung von Zahlen des Datentyps integer

Zahlen vom Datentyp integer lassen sich auch ausgeben mit dem Formatbeschreiber
  • Bw
  • Ow
  • Zw
Dabei entspricht
  • B = Binärdarstellung (Zahlensystem-Basis 2)
  • O = Octaldarstellung (Zahlensystem-Basis 8)
  • Z = Hexadezimaldarstellung (Zahlensystem-Basis 16)

  • w = Feldbreite

Beispiel: formate_integer.f90, Bildschirmausgabe: formate_integer.erg

Formatbeschreiber für den Datentyp real

Für die formatierte Ausgabe einer Zahl des Datentyps real kann man wählen zwischen

  • Fw.d bzw. rFw.d (Fixpunktzahl)
  • Ew.d bzw. rEw.d (Darstellung mit Exponenten zur Basis 10)
  • ESw.d bzw. rESw.d ("echte" wissenschaftliche Darstellung mit Exponenten zur Basis 10, Fortran 90/95)
  • ENw.d bzw. rENw.d (Ingenieur-Darstellung, der Exponent zur Basis 10 ist ein Vielfaches von 3, Fortran 90/95)
  • Gw.d bzw. rGw.d (Gleitpunktzahl)

  • w = Feldbreite
  • d = Anzahl der Nachkommastellen
  • r = Wiederholungsfaktor

Dabei gelten folgende Bedingungen zwischen w und d:
Fw.d Fixpunktdarstellung w >= d+2
Ew.d wissenschaftliche Darstellung w >= d+7
vor dem Dezimalpunkt steht bei den meisten Compilern als Ziffer eine 0, manchmal auch nichts
ESw.d "echte" wissenschaftliche Darstellung (Fortran90/95 Erweiterung) w >= d+7
vor dem Dezimalpunkt steht eine Ziffer zwischen 1 und 9
ENw.d "Ingenieur-Darstellung" (Fortran90/95 Erweiterung) w >= d+9
ähnlich b) und c), die Zahl vor dem Dezimalpunkt liegt zwischen 1 und 999 bzw. -1 und -999 und der Exponent zur Basis 10 wird stets als ein Vielfaches von 3 bzw. -3 dargestellt wie es den üblichen Benennungen als Kilo, Mega, Giga ... bzw. Milli, Mikro, Nano... entspricht
Gw.d "Gleitpunktzahl" je nach Größe der Zahl F oder E-Format w >= d+7 Falls sich die Zahl als Fixpunktzahl (F-Format) darstellen lässt, wird dieses verwendet, wenn nicht, wird die E - Darstellung eingesetzt)

Siehe auch format_real.f90, Bildschirmausgabe: format_real.erg.

Formatbeschreiber für den Datentyp logical

Der Formatbeschreiber zur Ausgabe von Werten des logischen Datentyps lautet
  • Lw bzw. rLw

  • w = Feldbreite
  • r = Wiederholungsfaktor

Bezüglich der Anwendung und der Wirkung betrachte man das folgende Beispiel:

format_logical.f90, Bildschirmausgabe: format_logical.erg.

Formatbeschreiber für den Datentyp character

Für Zeichenketten existieren als mögliche Formatbeschreiber A bzw. rA und Aw bzw. rAw, die sich ihrer Wirkung nach unterscheiden.

  • A bzw. rA oder
  • Aw bzw. rAw
  • w = Feldbreite
  • r = Wiederholungsfaktor

Verwendet man bei der Ausgabe einer Zeichenkette der Länge l als Formatangabe A, so wird die Zeichenkette in ihrer generischen Feldbreite l ausgegeben.

Schreibt man hingegen als Formatangabe Aw, so sind die Fälle w >= l und w < l zu unterscheiden. Ist die Zeichenkettenlänge kleiner als als die zur Ausgabe vorgesehene Feldbreite, so werden w-l Leerzeichen (Blanks) der Zeichenkette vorangestellt. Das Ausgabefeld wird von rechts her aufgefüllt. Passt die Zeichenkette allerdings nicht in die Ausgabefeldbreite hinein, so werden nur die ersten l Zeichen der Zeichenkette ausgegeben.

Zusatzbemerkung: Wurde zur Ausgabe einer Zeichenkette das listengesteuerte Ausgabeformat verwendet, wird bei den meisten Compilern zunächst ein Leerzeichen und dann erst die Zeichenkette ausgegeben.

Beispiel zum formatierten Ausgeben von Zeichenketten: format_character.f90, Bildschirmausgabe: format_character.erg.

Formatbeschreiber zur Positionierung

Bei X und nX in der Formatangabe werden 1 bzw. n Leerzeichen bei der Ausgabe eingefügt.

Will man eine Ausgabe mit Hilfe eines Formatbeschreibers an eine bestimmte Spalte setzen (und so eine Art Tabulatorfunktion nutzen), kann man dies ab Fortran 90/95 mit Tc tun. Die Zahl c gibt die Spaltennummer an.

Einen Zeilenvorschub kann man mit / einbauen. Dementsprechend führt // in einer Formatbeschreiberkette dazu, dass eine Leerzeile ausgeben wird.

Will man den gegenteiligen Effekt erreichen und am Ende einer write-Anweisung den Zeilenvorschub unterdrücken, kann man an das Ende einer Formatbeschreiberkette $ anhängen.

Während die einzelnen Formatbeschreiber in einer Formatbeschreiberkette durch Kommatas getrennt werden müssen, kann man bei der Verwendung von / und $ diese auch weglassen.

Formatgesteuertes Einlesen von Werten

Genauso wie in den write-Anweisungen ließen sich auch beim read-Befehl Formatbeschreiber angeben.

In der Regel ist das listengesteuerte Einlesen von Werten, d.h. die read-Anweisung mit der Angabe von * als Formatangabe eine gute Wahl.

Während sich das listengesteuerte Einlesen von Werten vom Datentyp real mit read(*,*) extrem gutmütig verhält und alle Formate von real-Werten annimmt (sei es nun als Fixpunktzahl oder in der wissenschaftlichen Notation), birgt die Vorgabe eines festen Formatbeschreibers in diesem Fall die Gefahr, dass z.B. mit einem vorgebenen Formatbeschreiber für eine Fixpunktzahl der Anwender den Wert im wissenschaftlicher Notation angibt und dann Fehler auftreten. Haben Sie allerdings formatiert Wertetabellen mit Zahlen auf Dateien geschrieben, könnte es unter Umständen sinnvoll sein, wieder formatgesteuert einzulesen. Hier ist jedoch unbedingt auf eine exakte Gleichheit der Formatbeschreiber beim Wertetabellen-Schreiben mit denen beim Wertetabellen-Lesen zu achten, weil sonst Fehler auftreten könnten.

Formatiert geschriebene Wertetabellen mit Werten des Datentyps real können jedoch in Fortran immer listengesteuert eingelesen werden.

Das interaktive formatgesteuerte Einlesen von Werten ist meines Erachtens nur für Zeichenketten (engl. strings) oder logische Werte sinnvoll.

Hinweis:
Insbesondere Zeichenketten, die Leerzeichen enthalten können, sollten formatiert eingelesen werden, sonst würde das 1. Leerzeichen oder Komma als Ende-Markierung der Zeichenkette angesehen werden und es würde nur der Anfangsteil der Zeichenkette als Variablenwert zugewiesen werden.

Man liest also Zeichenketten am besten mit einer Formatangabe ein, z.B.

   read(*,'(A)') zeichenkette

Beispiel: zeichenkette_einlesen.f90, Bildschirmausgabe: zeichenkette_einlesen.erg

Selten dürfte sein, dass logische Werte eingelesen werden sollen. Hier macht die Format-Angabe für logische Werte (L) insofern Sinn, als dass dadurch vom Anwender explizit ein logischer Wert bei der Eingabe mit T oder F angegeben werden muss und mit Hilfe von iostat in der read-Anweisung falsche Eingabewerte leichter erkannt und abgefangen werden können. Das unterschiedliche Verhalten beim unformatierten und beim formatierten Lesen logischer Werte - insbesondere wenn man statt T oder F z.B. 0 oder B angibt - kann das Verhalten z.B. mit read_logical.f90 getestet werden.

Umwandlung eines real-Wertes in eine Zeichenkette und umgekehrt (internal files)

In Fortran existiert ein spezieller Mechanismus als Erweiterung des bisher kennengelernten Verfahrens der Ein- und Ausgabe. Im Englischen wird dieser Mechanismus als internal files bezeichnet.

Statt mit write auf eine externe Datei (oder den Bildschirm) zu schreiben, kann man damit stattdessen intern in den Speicherbereich einer Variablen schreiben, um so z.B. einen real-Wert in eine Zeichenkette (character) zu verwandeln.

Der Mechanismus der internal files wird im folgenden Beispiel verwendet, um genau dieses zu tun. Zusätzlich wird im Beispiel das Format (der Formatbeschreiber) durch die Größe des zu konvertierenden Wertes bestimmt.

Beispiel: real_in_zeichenkette.f90
! Umwandlung von Werten des Datentyps real in
! eine Zeichenkette (character)
! gemaess eines von der Zahl abhaengigen Formatbeschreibers

! Demonstration des Mechanismus der sogenannten "internal files"

program real_in_zeichenkette
   implicit none
   real :: x
   character(len=9)  :: fb
   character(len=12) :: zeichenkette

   write(*,'(1X,A$)') 'Geben Sie bitte einen Wert vom Datentyp real ein: '
   read(*,*) x
   write(*,*) 'Ihre Eingabe: x = ', x
   write(*,*)

   ! Je nach Groesse der eingelesenen Zahl wird nun der Formatbeschreiber fb
   ! festgelegt. Die Feldbreite jedes Formats wurde entsprechend der Laenge
   ! des Strings Zeichenkette gewaehlt

   if ( x > 9999999.0) then
      fb = '(ES12.5)'
   else if ( x < -999999.0) then
      fb = '(ES12.5)'
   else if ( x == 0) then
      fb = '(F12.4)'
   else if ( abs(x) < 0.01 ) then
      fb = '(ES12.5)'
   else
      fb = '(F12.4)'
   end if
   
   ! Umwandlung von x aus dem Datentyp real in einen String 
    
   write(zeichenkette,fb) x 
   
   ! Bildschirmausgabe der Zeichenkette
   
   write(*,*) "Es wird nun 'x_'//zeichenkette//'_x' ausgegeben: ", 'x_'//zeichenkette//'_x'

end program real_in_zeichenkette

Werte-Beispiele:


 Geben Sie bitte einen Wert vom Datentyp real ein: 987777
 Ihre Eingabe: x =    987777.0    
 
 Es wird nun 'x_'//zeichenkette//'_x' ausgegeben: x_ 987777.0000_x

Geben Sie bitte einen Wert vom Datentyp real ein: -9876543 Ihre Eingabe: x = -9876543. Es wird nun 'x_'//zeichenkette//'_x' ausgegeben: x_-9.87654E+06_x
Geben Sie bitte einen Wert vom Datentyp real ein: 0.001 Ihre Eingabe: x = 1.0000000E-03 Es wird nun 'x_'//zeichenkette//'_x' ausgegeben: x_ 1.00000E-03_x
Geben Sie bitte einen Wert vom Datentyp real ein: -0.001 Ihre Eingabe: x = -1.0000000E-03 Es wird nun 'x_'//zeichenkette//'_x' ausgegeben: x_-1.00000E-03_x

Genauso wie mit

    write(<Variable_2>,<Formatangabe>) <Variable_1> 
der Inhalt aus dem Speicherbereich von Variable_1 unter Beachtung der Formatangabe in den Speicherbereich der Variable_2 geschrieben werden kann, lässt sich die umgekehrte Operation mit read realisieren.

Beispiel: internal_file_read.f90
program internal_file_read
   implicit none
   character(len=15) :: werte = '1.234 2.4e-6'
   real :: x, y
   
   read(werte,*) x, y
   
   write(*,*) 'intern wurden zugewiesen: '
   write(*,*) 'x = ', x
   write(*,*) 'y = ', y

end program internal_file_read

Bildschirmausgabe:
 intern wurden zugewiesen: 
 x =    1.234000    
 y =   2.4000001E-06

Zu beachten ist, dass als erstes Argument der internal file-read- bzw. write-Anweisung mit den Namen einer Variablen auf den Speicherplatz dieser Variablen referenziert wird und hier nur der Name einer Variablen angegeben werden kann, während als Argumente der Name einer oder mehrerer Variablen stehen können.

Zurück zur Vorlesungsseite


Heidrun.Kolinsky@uni-bayreuth.de
(Dr. Heidrun Kolinsky, Rechenzentrum der Universität Bayreuth, Gebäude NW2, Raum 159, Universitätsstraße 30, D-95440 Bayreuth, Tel. 0921/55-2687)