Having covered the detailed mechanics of allocating and deallocating arrays, including the built-in error handling, we finally arrive at implementing the CSV file reader subroutine, as shown in the following listing.
Listing 5.10 Reading stock price data from CSV files and storing them into arrays
subroutine read_stock(filename, time, open, high,&
low, close, adjclose, volume)
...
integer :: fileunit
integer :: n, nm
nm = num_records(filename) - 1 ❶
if (allocated(time)) deallocate(time) ❷
allocate(character(10) :: time(nm)) ❷
call alloc(open, nm) ❸
call alloc(high, nm) ❸
call alloc(low, nm) ❸
call alloc(close, nm) ❸
call alloc(adjclose, nm) ❸
call alloc(volume, nm) ❸
open(newunit=fileunit, file=filename) ❹
read(fileunit, fmt=*, end=1) ❺
do n = 1, nm
read(fileunit, fmt=*, end=1) time(n), open(n),& ❻
high(n), low(n), close(n), adjclose(n), volume(n) ❻
end do
1 close(fileunit) ❼
end subroutine read_stock
❶ Finds the number of records (lines) in a file
❷ Allocates the array of timestamps
❸ Allocates the stock price data arrays
❺ Skips the data header in the first line
❻ Reads the data line-by-line and stores them into arrays
To find the length of the arrays before I allocate them, I inquire about the length of the CSV file using a custom function num_records, defined in src/mod_io.f 90. If you’re wondering what the number 1 means in the 1 close(fileunit), it’s just a line label that Fortran uses if and when it encounters an exception in the read(fileunit, fmt=*, end=1) statements. If you’re interested in how this function works, take a look inside src/mod_io.f 90. I won’t spend much time on the I/O-specific code here, as we just need it to move forward with the array analysis. We’ll explore I/O in more detail in chapter 6.
On every subroutine entry, the arrays time, open, high, low, close, adjclose, and volume will be allocated with size nm. The subroutine alloc now seamlessly reallocates the arrays for us. Notice that we still use the explicit way of allocating and deallocating the array of timestamps. This is because we implemented the convenience subroutines alloc and free that work on real arrays. Because of Fortran’s strong typing discipline, we can’t just pass an array of strings to a subroutine that expects an array of reals. We’ll learn in chapter 9 how to write generic procedures that take arguments of different types. For now, explicitly allocating the array of timestamps will do. Furthermore, we also need to specify the string length when allocating the time array.
Having read the CSV files and loaded the stock price arrays with the data, we can move on to the actual analysis and fun with arrays.
Getting the number of lines in a text file
If you’re curious how the num_records function is implemented, take a look at src/mod_io.f90. This function opens a file and counts the number of lines by reading it line-by-line.