Einen Zahlensatz aus einer Datei extrahieren

680
Daniel Standage

Ich programmiere hauptsächlich in C / C ++ und Perl, aber ich lerne gerade Fortran. Ich begann damit, ein einfaches Programm zu programmieren, etwas, das mich in Perl <5 Minuten brauchte (siehe diesen Thread von BioStar). Nachdem ich mehrere Stunden an einer Lösung in Fortran 95 gearbeitet hatte (ich lernte mich dabei), kam ich zu dieser Lösung.

impliziert keine

! Variable definitions
integer                 :: argc
integer                 :: num_digits
integer                 :: i, j
integer                 :: iocode
integer                 :: line_length
integer                 :: value
character ( len=256 )   :: infile
character ( len=16 )    :: int_format
character ( len=16 )    :: int_string
character ( len=2048 )  :: line

! Verify and parse command line arguments
argc = iargc()
if( argc < 1 ) then
  write(*, '(A, I0, A)') "Error: please provide file name (argc=", argc, ")"
  stop
endif
call getarg(1, infile)

! Open input file, croak if there is an issue
open( unit=1, file=infile, action='read', iostat=iocode )
if( iocode > 0 ) then
  write(*, '(A, A, A)') "Error: unable to open input file '", trim(infile), "'"
  stop
endif

! Process the file, print in CSV format
do while(1 == 1)
  ! Read the line, skip if it is empty
  100 read( unit=1, fmt='(A)', end=200 ) line
  line_length = len_trim(line)
  if( line_length == 0 ) then
    goto 100
  endif

  ! The first value in the line is a string
  ! Find string boundaries and print it out
  i = 0
  do while( line(i:i) == ' ' )
    i = i+1
  end do
  j = i
  do while( line(j:j) /= ' ' )
    j = j+1
  end do
  write(*, '(A)', advance="no") line(i:j-1)

  ! Now grab the rest of the integer values
  ! on the line, multiply them by 3, and print
  i = j
  j = 0
  do while( i < line_length)
    do while( line(i:i) == ' ' )
      i = i+1
    end do
    j = i
    do while( j < line_length .and. line(j:j) /= ' ' )
      j = j+1
    end do
    int_string = line(i:j-1)
    read(int_string, '(I)') value
    value = value*3
    write(*, '(A, I0)', advance="no") ",", value
    i = j
    j = 0
  end do

  print *
end do
200 close( 1 )

end program

Es gibt viele Dinge, die mir nicht gefallen, wie ich dieses Programm geschrieben habe, aber es ist schwer, meine Unerfahrenheit mit der neuen Syntax von der schlechten Praxis zu trennen. Eine Sache, die ich nicht mag, ist die Verwendung von Etiketten und die infame gotoAussage. Ich bin an jeglichem Feedback interessiert, aber ich bin besonders an besseren Möglichkeiten interessiert, mit der Kontrollstruktur des Programms umzugehen (ohne die gotoAnweisung und den endParameter in der readFunktion zu verwenden).

Die Compiler, auf die ich nur über F95 zugreifen kann, haben Zugriff.

Antworten
12

2 Antworten auf die Frage

7

Sie können die GOTOAnweisungen loswerden, indem Sie beschriftete Schleifen mit EXITund CYCLEAnweisungen verwenden. Damit können Sie die Art der Flusssteuerung ausführen, für die Sie die GOTOs verwendet haben, aber nicht die weniger wünschenswerten Funktionen, wie z. B. berechnete GOTOs. Durch die Verwendung dieser neueren Fortran-Funktionen erhalten Sie auch eine klarere Vorstellung von der Flusssteuerung an dem Punkt, an dem die Verzweigung erfolgt. ZB Anweisungen CYCLEoder EXITAnweisungen senden Sie den Quellcode weiter nach oben bzw. unten, und ein gut gewähltes Etikett kann angeben, was die Verzweigung zu erreichen versucht.

Ein weiterer Vorschlag, auch wenn dies eher eine persönliche Präferenz ist, besteht darin, die potenziell endlose DO WHILE(1 == 1)Schleife zu vermeiden, indem eine normale DOSchleife mit einem hohen oberen Grenzwert verwendet und eine Warnmeldung hinzugefügt wird, wenn dieser Grenzwert erreicht wird, bevor das Dateiende erreicht wird. Viele Leute finden das allerdings zu pingelig.

Beispielcode, der diese beiden Punkte zeigt:

PROGRAM so_goto

  IMPLICIT NONE

  INTEGER,PARAMETER :: line_max = 100000

  INTEGER :: i, ios, line_length
  CHARACTER( len=2048 ) :: line

  OPEN(UNIT=10,FILE="so_goto.txt",ACTION="read")

  fread: DO i=1,line_max
    READ(UNIT=10,FMT='(A)',IOSTAT=ios) line
    IF( ios < 0 ) EXIT fread
    line_length = LEN_TRIM(line)
    IF( line_length == 0 ) CYCLE fread

    IF( i == line_max ) THEN
      print*,"BTW, you've hit your read limit before the end of the file."
    ENDIF
  ENDDO fread
  CLOSE(10)

END PROGRAM so_goto
+1 Ich * viel * ziehe die `EXIT / CYCLE`-Syntax den` GOTO'-Anweisungen vor. Es macht die Absicht der Aussagen viel deutlicher. Daniel Standage vor 9 Jahren 0
Verwenden Sie entweder eine normale do oder wenn Sie eine Endlosschleife verwenden, reicht es aus, nur "do" zu tun. Der "while" -Teil ist überflüssig und sollte nur verwendet werden, wenn eine reale Bedingung erforderlich ist. Vladimir F vor 7 Jahren 1
2
winwaed

Ich habe in meiner Zeit ein ziemlich unangenehmes Fortran gesehen: Forschungsabteilungen scheinen die schlechtere Quelle zu sein! Ihr Code ist dem Code weit überlegen!

Damals benutzten wir eine erweiterte Form von Fortran 77 mit einigen strukturierten Erweiterungen und Formatierungs-Feinheiten. Es gab keine Umgehung von GOTOs und FORMATs und deren Bezeichnungen. Ich habe versucht, sie auf ein Minimum zu beschränken und sie nur für Break / Cycle-Sprünge zu verwenden, wie Sie es tun. Obwohl mir moderne Fortran-Erfahrung fehlt, scheinen Sie auf dem richtigen Weg zu sein.

Danke für die Bestätigung! Ich musste etwas kichern, als ich von "modernem Fortran-Erlebnis" sprach. :) Auf dem neuesten Stand zu sein, ist definitiv NICHT, warum ich Fortran lerne! Daniel Standage vor 9 Jahren 1