9

I would like to use deferred-length character strings in a "simple" manner to read user input. The reason that I want to do this is that I do not want to have to declare the size of a character string before knowing how large the user input will be. I know that there are "complicated" ways to do this. For example, the iso_varying_string module can be used: https://www.fortran.com/iso_varying_string.f95. Also, there is a solution here: Fortran Character Input at Undefined Length. However, I was hoping for something as simple, or almost as simple, as the following:

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

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

Also, my_string has not been allocated. Why?

Why isn't this a simple feature of Fortran? Do other languages have this simple feature? Am I lacking some basic understanding about this issue in general?

Community
  • 1
  • 1
David Hansen
  • 501
  • 4
  • 14

4 Answers4

17

vincentjs's answer isn't quite right.

Modern (2003+) Fortran does allow automatic allocation and re-allocation of strings on assignment, so a sequence of statements such as this

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

is correct and will work as expected and write out 3 strings of different lengths. At least one compiler in widespread use, the Intel Fortran compiler, does not engage 2003 semantics by default so may raise an error on trying to compile this. Refer to the documentation for the setting to use Fortran 2003.

However, this feature is not available when reading a string so you have to resort to the tried and tested (aka old-fashioned if you prefer) approach of declaring a buffer of sufficient size for any input and of then assigning the allocatable variable. Like this:

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

No, I don't know why the language standard forbids automatic allocation on read, just that it does.

High Performance Mark
  • 77,191
  • 7
  • 105
  • 161
9

Deferred length character is a Fortran 2003 feature. Note that many of the complicated methods linked to are written against earlier language versions.

With Fortran 2003 support, reading a complete record into a character variable is relatively straight forward. A simple example with very minimal error handling below. Such a procedure only needs to be written once, and can be customized to suit a user's particular requirements.

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
IanH
  • 21,026
  • 2
  • 37
  • 59
  • I mentioned in my question that I am looking for something simpler than the answer that I have posted at [Fortran Character Input at Undefined Length](http://stackoverflow.com/questions/14886787/fortran-character-input-at-undefined-length). Isn't your subroutine the same as the answer that I posted at [Fortran Character Input at Undefined Length](http://stackoverflow.com/questions/14886787/fortran-character-input-at-undefined-length)? – David Hansen Jun 29 '15 at 21:53
  • It is a similar technique. Note that question was tagged Fortran 95 - deferred length character was introduced with Fortran 2003. Calling a two argument subroutine is a fairly simple exercise. – IanH Jun 29 '15 at 23:18
0

Deferred length arrays are just that: deferred length. You still need to allocate the size of the array using the allocate statement before you can assign values to it. Once you allocate it, you can't change the size of the array unless you deallocate and then reallocate with a new size. That's why you're getting a debug error.

Fortran does not provide a way to dynamically resize character arrays like the std::string class does in C++, for example. In C++, you could initialize std::string var = "temp", then redefine it to var = "temporary" without any extra work, and this would be valid. This is only possible because the resizing is done behind the scenes by the functions in the std::string class (it doubles the size if the buffer limit is exceeded, which is functionally equivalent to reallocateing with a 2x bigger array).

Practically speaking, the easiest way I've found when dealing with strings in Fortran is to allocate a reasonably large character array that will fit most expected inputs. If the size of the input exceeds the buffer, then simply increase the size of your array by reallocateing with a larger size. Removing trailing white space can be done using trim.

vincentjs
  • 161
  • 1
  • 7
  • 2
    True, but worth noing that re-allocation on assignment simplifies a lot of things. Notably, your C++ exaple is equally simple in Fortran. The compiler could even optoimize it to call `realloc()` under the hood, but I don't know if any does that. – Vladimir F Героям слава Jun 27 '15 at 09:25
0

You know that there are "complicated" ways of doing what you want. Rather than address those, I'll answer your first two "why?"s.

Unlike intrinsic assignment a read statement does not have the target variable first allocated to the correct size and type parameters for the thing coming in (if it isn't already like that). Indeed, it is a requirement that the items in an input list be allocated. Fortran 2008, 9.6.3, clearly states:

If an input item or an output item is allocatable, it shall be allocated.

This is the case whether the allocatable variable is a character with deferred length, a variable with other deferred length-type parameters, or an array.

There is another way to declare a character with deferred length: giving it the pointer attribute. This doesn't help you, though, as we also see

If an input item is a pointer, it shall be associated with a definable target ...

Why you have no output from your write statement is related to why you see that the character variable isn't allocated: you haven't followed the requirements of Fortran and so you can't expect the behaviour that isn't specified.


I'll speculate as to why this restriction is here. I see two obvious ways to relax the restriction

  • allow automatic allocation generally;
  • allow allocation of a deferred length character.

The second case would be easy:

If an input item or an output item is allocatable, it shall be allocated unless it is a scalar character variable with deferred length.

This, though, is clumsy and such special cases seem against the ethos of the standard as a whole. We'd also need a carefully thought out rule about alloction for this special case.

If we go for the general case for allocation, we'd presumably require that the unallocated effective item is the final effective item in the list:

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

and then we have 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

It gets quite messy very quickly. Which seems like a lot of problems to resolve where there are ways, of varying difficulty, of solving the read problem.

Finally, I'll also note that allocation is possible during a data transfer statement. Although a variable must be allocated (as the rules are now) when appearing in input list components of an allocated variable of derived type needn't be if that effective item is processed by defined input.

Community
  • 1
  • 1
francescalus
  • 30,576
  • 16
  • 61
  • 96