Using a deferred character string to read user input

I would like to use deferred character strings in a “simple” form to read user input. The reason I want to do this is because I don't want to declare the size of the character string before I know how big the user input will be. I know that there are “complicated” ways to do this. For example, you can use the iso_varying_string module: https://www.fortran.com/iso_varying_string.f95 . In addition, there is a solution: Fortran symbol input in Undefined Length . However, I was hoping for something simple or almost simple:

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

When I run this program, the output is:

 ./a.out here is the user input F 32765 

Note that there is no result from write(*,'(a)') my_string . Why?

In addition, my_string not allocated. Why?

Why is this not a simple Fortran feature? Do other languages ​​have this simple function? Am I lacking a basic understanding of this problem in general?

+5
source share
4 answers

Vincentjs answer is not quite right.

Modern (2003+) Fortran allows you to automatically distribute and redistribute strings when assigned, so a sequence of instructions such as

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

is correct and will work as expected and write 3 lines of different lengths. At least one compiler commonly used, the Intel Fortran compiler, does not use the semantics of 2003 by default, so an error may occur when trying to compile this. Refer to the documentation for customization to use Fortran 2003.

However, this function is not available when reading a line, so you have to resort to a proven (according to the old, if you like) approach to declare a buffer of sufficient size for any input, and then assign a distributed variable. Like this:

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

No, I don’t know why the locale forbids automatic distribution to read , just like that.

+9
source

The lazy character is a feature of Fortran 2003. Note that many of the complex methods involved are written in relation to earlier language versions.

With Fortran 2003 support, reading a full write to a character variable is relatively straightforward. A simple example with minimal error handling below. Such a procedure should be written only once and can be configured in accordance with the specific requirements of the user.

 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 
+6
source

Deferred length arrays are simple: deferred length. You still need to allocate the size of the array using the allocate operator before you can assign values ​​to it. After allocating it, you cannot resize the array unless you deallocate and then re allocate with the new size. This is why you get a debugging error.

Fortran does not provide a way to dynamically change character arrays, such as the std::string class in C ++. In C ++, you can initialize std::string var = "temp" and then override it to var = "temporary" without any extra work, and that will be valid. This is only possible because resizing is done behind the scenes using the functions of the std::string class (it doubles the size if the buffer limit is exceeded, which is functionally equivalent to re allocate ing with a larger 2x array).

Practically speaking, the easiest way I found when working with strings in Fortran is to allocate a fairly large array of characters that will fit most expected inputs. If the size of the input exceeds the buffer, simply increase the size of your array using allocate with a larger size. Removing a trailing space can be done with trim .

0
source

You know that there are “complicated” ways to do what you want. Instead of addressing them, I will answer your first two questions “why?”.

Unlike the built-in assignment, the read statement does not have a target variable, first assigned to the correct size and type parameters for the incoming thing (if it is not already). In fact, it is a requirement that items in the input list are highlighted. Fortran 2008, 9.6.3, clearly states:

If an input or output element is allocated, it must be highlighted.

This is the case whether the distributed variable is a delayed character, a variable with other parameters of the length of the delayed length, or an array.

There is another way to declare a character with a deferred length: assign the pointer attribute to it. However, this will not help you, as we also see

If the input element is a pointer, it must be associated with a definable target ...

Why you don’t have output from your write statement is due to why you see that the character variable is not distributed: you have not met Fortran requirements and therefore cannot expect behavior that isn’t "t.


I will ponder why this limitation is here. I see two obvious ways to ease the restriction

  • Allow automatic distribution as a whole;
  • allows you to select a symbol of deferred length.

The second case would be easy:

If an input or output element is allocated, it must be assigned, unless it is a scalar character variable with a deferred length.

This, however, is awkward, and such special cases seem to contradict the ideal of the standard as a whole. We will also need a carefully thought-out allusion rule for this special case.

If we move on to the general case for distribution, we presumably require the unallocated effective item to be the last effective item in the list:

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

and then we need to worry about

 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 

He is very confused very fast. It seems that many problems are solved where there are ways, with varying difficulty, to solve the problem of reading.

Finally, I also note that distribution is possible during a data transfer instruction. Although the variable should be allocated (as the rules apply now), if the derived variable is not necessarily included in the list of input components of the selected variable, if this effective element is processed by a specific input.

0
source

All Articles