2011-06-29 10 views
10

Jestem nowy w firmie Fortran i chciałbym móc napisać dwuwymiarową tablicę do pliku tekstowego, w sposób zgodny z wierszami (odstępy pomiędzy kolumnami i każdy wiersz w osobnej linii). Próbowałem następujące, i wydaje się działać w następujący prosty przykład:W Fortranie 90, jaki jest dobry sposób na zapisanie tablicy do pliku tekstowego, wierszowo?

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 

Jak powiedziałem, to wydaje się działać dobrze w tym prostym przykładzie: wynikowy plik tekstowy, aoutput.txt, zawierającego liczby 1-762 na linii 1, numery 763-1524 na linii 2 i tak dalej.

Ale kiedy używam powyższych pomysłów (tj. Od ostatniej piątej do ostatniej, czwartej do ostatniej, trzeciej do ostatniej i drugiej do ostatniej linii kodu powyżej) w bardziej skomplikowanym program, wpadam w kłopoty; każdy wiersz jest ograniczony (przez nową linię) tylko sporadycznie, jak się wydaje. (Nie pisałem i prawdopodobnie nie będę publikował tutaj całego mojego skomplikowanego programu/skryptu - ponieważ jest dość długi.) Brak spójnych ograniczników wierszy w moim skomplikowanym programie/skrypcie prawdopodobnie sugeruje kolejny błąd w moim kodzie, nie z czteroliniowa procedura zapisu do pliku powyżej, ponieważ powyższy prosty przykład wydaje się działać poprawnie. Mimo to zastanawiam się, czy możesz mi pomóc, jeśli pomyślę o lepszym pisaniu do pliku w formacie, którego powinienem użyć?

Dziękuję bardzo za poświęcony czas. Bardzo to doceniam.

+1

Metoda, której używasz wydaje mi się w porządku. Mówisz, że prawdziwy program jest bardziej skomplikowany, ale jedyne, co ma znaczenie, to specyfikacja tablicy i nie widzę, jak to może być bardziej skomplikowane, tylko większe. Czy jesteś pewien, że granice (numrows, numcols) używane w tym programie są poprawne? – eriktous

+1

Czy możesz przedstawić dość mały program przykładowy, który odtwarza chore zachowanie, które napotykasz? Przykład jest dość dobry i nie widząc niczego "konkretnego", trudno jest spekulować na temat ewentualnych błędów w twoim większym programie. – Rook

Odpowiedz

12

Istnieje kilka kwestii tutaj.

Podstawowe jest to, że nie należy używać tekstu jako formatu danych dla znacznych porcji danych. Jest duży i jest powolny. Wynik jest dobry dla czegoś, co sam będziesz czytać; nie usiądziesz z wydrukiem 3,81 miliona liczb całkowitych i przejrzysz je. Jak pokazuje poniższy kod, prawidłowe wyjście tekstowe jest około 10 razy wolniejsze, a 50% większe niż wyjście binarne. Jeśli przejdziesz do wartości zmiennoprzecinkowych, występują problemy z precyzją strat z użyciem ciągów ASCII jako formatu wymiany danych. itp.

Jeśli celem jest wymiana danych z matlab, dość łatwo zapisać dane w formacie, który może odczytać matlab; możesz użyć matOb/matPutVariable API z matlab lub po prostu napisać to jako macierz HDF5, którą Matlab może przeczytać. Lub możesz po prostu napisać tablicę w surowym binarnym Fortranie jak poniżej i mieć matlab read it.

Jeśli musisz używać ascii do wypisywania ogromnych tablic (które, jak wspomniano, jest złym i powolnym pomysłem), to masz problemy z domyślnymi długościami rekordów w IO z poprawioną listą. Najlepiej jest wygenerować w środowisku wykonawczym ciąg formatujący, który poprawnie opisuje twoje dane wyjściowe, a najbezpieczniejszym na tym poziomie dla tak dużych (~ 5000 znaków szerokości!) Linii jest ustawienie długości rekordu jawnie na coś większego niż to, co będziesz drukować tak, że fortranowa biblioteka IO nie pomaga w dzieleniu linii.

W poniższym kodzie

WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))' 

generuje ciąg rowfmt która w tym przypadku byłoby (762(1X,I6)), który jest formatem będziesz używać do drukowania, a opcja OPENRECL ustawia długość rekordu do być czymś większym niż 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 
3

Używanie "*" jest skierowane do listy IO - Fortran podejmie decyzję za Ciebie. Niektóre zachowania nie są określone. Możesz uzyskać większą kontrolę za pomocą instrukcji formatowania. Jeśli chcesz pozytywnie zidentyfikować granice wierszy, po każdym rzędzie wypiszesz znacznik. Coś jak:

DO i=1,numrows 
    WRITE(12,*) a(i,:) 
    write (12, '("X")') 
    END DO 

Dodatek kilka godzin później:

może z dużymi wartościami numcols linie są zbyt długie dla niektórych programów, które używasz do zbadania plik? Na rachunku wyjściowego, spróbuj:

WRITE(12, '(10(2X, I11))') a(i,:) 

który złamie każdego wiersza macierzy, jeśli ma więcej niż 10 kolumn, na wiele, krótsze linie w pliku.

10

To może być bardzo rondo i czasochłonny sposób to zrobić, ale i tak ... Można po prostu wydrukować każdy element tablicy osobno, używając advance='no' (tłumić wstawienie znaku nowej linii po co było drukowane) w Twoja instrukcja write. Kiedy skończysz linię, użyjesz "normalnej" instrukcji write, aby uzyskać znak nowego wiersza, i zacznij od nowa w następnym wierszu. Oto mały przykład:

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 

Po uruchomieniu tego programu, wyjście jest w następujący sposób:

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 
Powiązane problemy