2015-06-27 9 views
5

Vorrei utilizzare stringhe di caratteri di lunghezza differita in un modo "semplice" per leggere l'input dell'utente. La ragione per cui voglio farlo è che non voglio dover dichiarare la dimensione di una stringa di caratteri prima di sapere quanto grande sarà l'input dell'utente. So che ci sono modi "complicati" per farlo. Ad esempio, è possibile utilizzare il modulo iso_varying_string: https://www.fortran.com/iso_varying_string.f95. Inoltre, c'è una soluzione qui: Fortran Character Input at Undefined Length. Tuttavia, speravo in qualcosa di semplice, o quasi come semplici, come il seguente:Utilizzo di una stringa di caratteri con lunghezza differita per leggere l'input dell'utente

program main 

    character(len = :), allocatable :: my_string 
    read(*, '(a)') my_string 
    write(*,'(a)') my_string 
    print *, allocated(my_string), len(my_string) 

end program 

Quando ho eseguito questo programma, l'output è:

./a.out 
here is the user input 

F  32765 

Si noti che non v'è alcuna uscita dal write(*,'(a)') my_string. Perché?

Inoltre, my_string non è stato assegnato. Perché?

Perché questa non è una semplice funzione di Fortran? Altre lingue hanno questa semplice funzione? Mi mancano alcune conoscenze di base su questo problema in generale?

risposta

9

La risposta di vincentjs non è giusta.

moderna (2003+) Fortran fa consentire l'assegnazione automatica e riallocazione di stringhe di assegnazione, quindi una sequenza di istruzioni come questo

character(len=:), allocatable :: string 
... 
string = 'Hello' 
write(*,*) 
string = 'my friend' 
write(*,*) 
string = 'Hello '//string 
write(*,*) 

è corretto e funziona come previsto e scrivere su 3 corde di diverse lunghezze. Almeno un compilatore in uso diffuso, il compilatore Intel Fortran, non utilizza la semantica 2003 per impostazione predefinita, pertanto potrebbe generare un errore durante il tentativo di compilazione. Fare riferimento alla documentazione per l'impostazione per utilizzare Fortran 2003.

Tuttavia, questa funzione non è disponibile durante la lettura di una stringa, quindi è necessario ricorrere al metodo collaudato (o vecchio stile se si preferisce) di dichiarare un buffer di dimensioni sufficienti per ogni input e quindi assegnare la variabile allocatable.Come questo:

character(len=long) :: buffer 
character(len=:), allocatable :: string 
... 
read(*,*) buffer 
string = trim(buffer) 

No, non so il motivo per cui il linguaggio standard vieta assegnazione automatica su read, solo che lo fa.

0

Gli array di lunghezza differita sono proprio questo: lunghezza differita. È ancora necessario allocare la dimensione dell'array usando l'istruzione allocate prima di poter assegnare valori ad essa. Una volta che lo si assegna, non è possibile modificare la dimensione della matrice a meno che non si sia deallocate e quindi sia allocate con una nuova dimensione. Ecco perché stai ricevendo un errore di debug.

Fortran non fornisce un modo per ridimensionare dinamicamente gli array di caratteri come ad esempio la classe std::string in C++. In C++, è possibile inizializzare std::string var = "temp", quindi ridefinirlo in var = "temporary" senza alcun lavoro aggiuntivo e ciò sarebbe valido. Ciò è possibile solo perché il ridimensionamento viene eseguito dietro le quinte dalle funzioni nella classe std::string (raddoppia la dimensione se viene superato il limite del buffer, che è funzionalmente equivalente a re allocate con un array 2x più grande).

In pratica, il modo più semplice che ho trovato quando ho a che fare con le stringhe in Fortran è di allocare un array di caratteri ragionevolmente grande che si adatti alla maggior parte degli input previsti. Se la dimensione dell'ingresso supera il buffer, aumentare semplicemente la dimensione dell'array di re allocate con una dimensione maggiore. La rimozione dello spazio bianco finale può essere eseguita utilizzando trim.

+1

Vero, ma vale la pena notare che la riassegnazione sull'assegnazione semplifica molte cose. In particolare, il tuo expo C++ è ugualmente semplice in Fortran. Il compilatore potrebbe persino optoimizzarlo per chiamare 'realloc()' sotto il cofano, ma non so se lo faccia. –

0

Sapete che ci sono modi "complicati" per fare ciò che volete. Piuttosto che indirizzare quelli, risponderò ai tuoi primi due "perché?" S.

Diversamente dall'assegnazione intrinseca, un'istruzione read non ha la variabile di destinazione assegnata per la prima volta ai parametri di dimensione e tipo corretti per la cosa in arrivo (se non è già così). In effetti, è necessario che gli elementi in una lista di input vengano allocati. Fortran 2008, 9.6.3, afferma chiaramente:

Se un elemento di input o un elemento di output è assegnabile, deve essere assegnato.

Questo è il caso se la variabile assegnabile è un carattere con lunghezza differita, una variabile con altri parametri di tipo lunghezza differita o una matrice.

C'è un altro modo per dichiarare un carattere con lunghezza differita: assegnando l'attributo pointer. Questo non ti aiuta, anche se, come vediamo anche

Se un elemento di input è un puntatore, esso deve essere associato ad un obiettivo definibile ...

Perché non hai uscita dal la tua istruzione write è correlata al motivo per cui vedi che la variabile di carattere non è allocata: non hai seguito i requisiti di Fortran e quindi non puoi aspettarti il ​​comportamento che non è specificato.


Mi chiedo perché questa restrizione è qui. Vedo due modi ovvi per rilassare la restrizione

  • consentire l'assegnazione automatica in generale;
  • consente l'allocazione di un carattere di lunghezza differita.

Il secondo caso sarebbe facile:

Se un elemento di ingresso o un elemento di uscita è allocabile, essa è ripartita meno che sia una variabile di carattere scalare con lunghezza differita.

Questo, tuttavia, è maldestro e tali casi speciali sembrano contrari all'etica dello standard nel suo insieme. Avremmo anche bisogno di una regola attentamente studiata sull'allocazione per questo caso speciale.

Se andiamo per il caso generale di assegnazione, avremmo presumibilmente richiedono che l'oggetto non allocato efficace è l'elemento efficace finale nella lista:

integer, allocatable :: a(:), b(:) 
character(7) :: ifile = '1 2 3 4' 
read(ifile,*) a, b 

e poi ci si deve preoccupare

type aaargh(len) 
    integer, len :: len 
    integer, dimension(len) :: a, b 
end type 
type(aaargh), allocatable :: a(:) 
character(9) :: ifile = '1 2 3 4 5' 

read(ifile,*) a 

Diventa abbastanza caotico molto rapidamente.Che sembra un sacco di problemi da risolvere dove ci sono modi, di diversa difficoltà, di risolvere il problema di lettura.

Infine, noterò anche che l'allocazione è possibile durante un'istruzione di trasferimento dati. Sebbene una variabile debba essere allocata (come le regole sono ora) quando appare nei componenti dell'elenco di input di una variabile allocata di tipo derivato needn't be se quell'elemento efficace viene elaborato dall'input definito.

6

Il carattere di lunghezza differita è una funzionalità di Fortran 2003. Nota che molti dei complicati metodi collegati sono scritti contro versioni linguistiche precedenti.

Con il supporto di Fortran 2003, la lettura di un record completo in una variabile di carattere è relativamente semplice. Un semplice esempio con la gestione degli errori molto minimale di seguito. Tale procedura deve essere scritta una volta sola e può essere personalizzata in base alle esigenze particolari dell'utente.

PROGRAM main 
    USE, INTRINSIC :: ISO_FORTRAN_ENV, ONLY: INPUT_UNIT 
    IMPLICIT NONE 
    CHARACTER(:), ALLOCATABLE :: my_string 

    CALL read_line(input_unit, my_string) 
    WRITE (*, "(A)") my_string 
    PRINT *, ALLOCATED(my_string), LEN(my_string) 

CONTAINS 
    SUBROUTINE read_line(unit, line)  
    ! The unit, connected for formatted input, to read the record from. 
    INTEGER, INTENT(IN) :: unit 
    ! The contents of the record. 
    CHARACTER(:), INTENT(OUT), ALLOCATABLE :: line 

    INTEGER :: stat   ! IO statement IOSTAT result. 
    CHARACTER(256) :: buffer ! Buffer to read a piece of the record. 
    INTEGER :: size   ! Number of characters read from the file. 
    !*** 
    line = '' 
    DO 
     READ (unit, "(A)", ADVANCE='NO', IOSTAT=stat, SIZE=size) buffer 
     IF (stat > 0) STOP 'Error reading file.' 
     line = line // buffer(:size) 
     ! An end of record condition or end of file condition stops the loop. 
     IF (stat < 0) RETURN 
    END DO 
    END SUBROUTINE read_line 
END PROGRAM main 
+0

Nella mia domanda ho menzionato che sto cercando qualcosa di più semplice della risposta che ho postato in [Fortran Character Input a Undefined Length] (http://stackoverflow.com/questions/14886787/fortran-character-input-at- undefined-lunghezza). La tua subroutine non è la stessa risposta che ho postato in [Fortran Character Input a Undefined Length] (http://stackoverflow.com/questions/14886787/fortran-character-input-at-undefined-length)? –

+0

È una tecnica simile. Si noti che la domanda è stata contrassegnata con Fortran 95 - il carattere di lunghezza differita è stato introdotto con Fortran 2003. La chiamata a una subroutine a due argomenti è un esercizio abbastanza semplice. – IanH

+0

Sì, buon punto. –

Problemi correlati