Appearance
Files and Paths
Reading
f90
program read_a_file
!< Read a whole file.
use stringifor
implicit none
type(string), allocatable :: lines(:)
type(string) :: content
call read_file(file='notes.txt', lines=lines) ! one string for each line
print '(I0,A)', size(lines), ' lines, the last is "'//lines(size(lines))//'"'
call content%read_file(file='notes.txt') ! the whole file in one string
print '(I0,A,I0,A)', content%len(), ' characters, ', content%count(new_line('a')), ' line ends'
endprogram read_a_file$ readfile
3 lines, the last is "third line"
34 characters, 3 line ends| Procedure | What it reads |
|---|---|
call read_file(file, lines[, form][, iostat][, iomsg]) | a whole file, into an allocatable array with one string for each line |
call read_lines(unit, lines[, form][, iostat][, iomsg]) | the same, from a connected unit |
call s%read_file(file[, is_fast][, form][, iostat][, iomsg]) | a whole file into one string, line ends included |
call s%read_lines(unit[, form][, iostat][, iomsg]) | the same, from a connected unit |
call s%read_line(unit[, form][, iostat][, iomsg]) | one line from a connected unit |
The first two are procedures of the module, the others methods. is_fast=.true. reads the file as a stream, in one read.
Line by line:
f90
program read_line_by_line
!< Read a file one line at a time.
use stringifor
implicit none
type(string) :: line
integer :: unit, iostat, n
n = 0
open(newunit=unit, file='poem.txt', status='old', action='read')
do
call line%read_line(unit=unit, iostat=iostat)
if (iostat /= 0) exit ! the end of the file, or an error
n = n + 1
print '(I0,A)', n, ': ['//line//']'
enddo
close(unit)
endprogram read_line_by_line$ readline
1: [roses are red]
2: []
3: [violets are blue]iostat is zero when a line has been read, an empty one included; at the end of the file it is the end-of-file code (is_iostat_end(iostat) is true) and the string is left unchanged. A last line without a line terminator is read like the others.
Writing
f90
program write_a_file
!< Write strings to a file.
use stringifor
implicit none
type(string) :: lines(3)
lines(1) = 'bread'
lines(2) = 'milk'
lines(3) = 'coffee'
call write_file(file='shopping.txt', lines=lines)
print '(A)', 'written'
endprogram write_a_file$ cat shopping.txt
bread
milk
coffee| Procedure | What it writes |
|---|---|
call write_file(file, lines[, form][, iostat][, iomsg]) | an array of strings, one for each line |
call write_lines(unit, lines[, form][, iostat][, iomsg]) | the same, to a connected unit |
call s%write_file(file[, form][, iostat][, iomsg]) | one string, as it is |
call s%write_line(unit[, form][, iostat][, iomsg]) | one string to a connected unit, as one line |
call s%write_lines(unit[, form][, iostat][, iomsg]) | one string to a connected unit, each of the lines it contains as a line |
Through a unit already open:
f90
program unit_io
!< Lines written to and read from a unit already open.
use stringifor
implicit none
type(string) :: line, text
type(string), allocatable :: lines(:)
integer :: unit, l
open(newunit=unit, file='notes.txt', status='replace')
line = 'first line'
call line%write_line(unit=unit) ! one string, one line
text = 'second line'//new_line('a')//'third line'
call text%write_lines(unit=unit) ! one string, a line for each line it holds
call read_lines(unit=unit, lines=lines) ! rewinds the unit, one string for each line
close(unit, status='delete')
do l = 1, size(lines)
print '(I0,A)', l, ': '//lines(l)
enddo
endprogram unit_io$ unit_io
1: first line
2: second line
3: third lineUnformatted files
Every procedure accepts form='unformatted': the file is then read or written as a stream (access='stream'), the lines separated by the new-line character.
fortran
call read_file(file='data.bin', lines=lines, form='unformatted')
call write_file(file='data.bin', lines=lines, form='unformatted')Paths
f90
program path_pieces
!< The pieces of a file name.
use stringifor
implicit none
type(string) :: path
path = '/home/user/data/archive.tar.gz'
print '(A)', path%basedir()//''
print '(A)', path%basename()//''
print '(A)', path%extension()//''
print '(A)', path%basename(strip_last_extension=.true.)//''
print '(A)', path%basename(extension='.tar.gz')//''
endprogram path_pieces$ paths
/home/user/data
archive.tar.gz
.gz
archive.tar
archive| Method | Result |
|---|---|
basedir([sep]) | the directory of a path |
basename([sep][, extension][, strip_last_extension]) | the file name, optionally without an extension |
extension() | the last extension, with its dot |
sep is the separator of the directories, / by default. A file name without a directory has an empty basedir.
Listing files
f90
program list_files
!< The files matching a pattern.
use stringifor
implicit none
type(string) :: s
type(string), allocatable :: files(:)
integer :: f
call s%glob(pattern='data/*.csv', list=files)
do f = 1, size(files)
print '(A)', files(f)//''
enddo
endprogram list_files$ glob
data/cars.csvglob(pattern, list) returns the paths matching a pattern, with the rules of the shell; list is an allocatable array of strings or of deferred-length characters, allocated with zero size when nothing matches. It is also a procedure of the module, call glob(self, pattern, list).
Only the wildcards *, ? and [...] are special: any other character of the pattern, a space, a ;, a $, a quote or a leading -, is part of the names searched, never a command of the shell. A matching directory is listed itself, not its content.
WARNING
glob runs the ls command: it works on Unix-like systems only.
Matching names
f90
program wildcards
!< The names matching a wildcard pattern.
use stringifor
implicit none
type(string) :: names(5)
integer :: n
names(1) = 'report-2025.csv'
names(2) = 'report-2026.csv'
names(3) = 'report-2026.txt'
names(4) = 'notes.txt'
names(5) = 'Report-2026.csv'
do n = 1, size(names)
if (names(n)%match('report-202[5-9].csv')) print '(A)', names(n)//''
enddo
print '(5L2)', names%match('*.txt')
endprogram wildcards$ wildcards
report-2025.csv
report-2026.csv
F F T T Fmatch(pattern) tells whether the whole string matches a wildcard pattern, with no file system involved: it works on any system, on names already in memory. The wildcards are the ones of the shell, as Python fnmatch.fnmatchcase: * any sequence of characters, ? any single character, [seq] a character of seq (a-z is a range), [!seq] a character not in seq. The match is case-sensitive, there is no escape character (match a wildcard with a class, [*]) and a leading dot is not special. match is elemental: on an array of strings it gives an array of logicals.
Temporary names
f90
program temporary_name
!< A name for a temporary file.
use stringifor
implicit none
type(string) :: s, name
name = s%tempname(prefix='scratch-') ! a name not used by any file of the directory
print '(L1,1X,L1)', name%start_with('scratch-'), name%len() > len('scratch-')
endprogram temporary_name$ tempname
T Ttempname([is_file][, prefix][, path]) returns a name that no file (or, with is_file=.false., no directory) of path has, starting with prefix. The name changes at each call: the example prints only what does not.
Encoding
f90
program base64
!< Encode and decode in base64.
use stringifor
implicit none
type(string) :: s, code
s = 'Hello World'
code = s%encode(codec='base64')
print '(A)', code//''
print '(A)', code%decode(codec='base64')//''
endprogram base64$ encode_base64
SGVsbG8gV29ybGQ=
Hello Worldencode(codec) and decode(codec) take the name of the codec; base64 is the only one available. The decoded string has exactly the encoded length: its blanks are preserved, and the padding of the code can be omitted.
The name of the codec is not case sensitive. With an unknown codec the result is a not allocated string: check it with is_allocated().
Colours for the terminal
f90
program colours
!< Colours and styles for the terminal.
use stringifor
implicit none
type(string) :: s
s = 'error: the file is missing'
print '(A)', s%colorize(color_fg='red', style='bold_on')
s = 'warning: the file is empty'
print '(A)', s%colorize(color_fg='yellow')
s = 'done'
print '(A)', s%colorize(color_fg='green', style='underline_on')
endprogram colourscolorize([color_fg][, color_bg][, style]) returns a character: the string wrapped in the ANSI escape codes of the colours and of the style, whose names are the ones of FACE (red, green, yellow_intense, ...; bold_on, italics_on, underline_on, ...).