After completing this exercise, you will be able to:
allocate and deallocateCommand-line arguments allow users to pass information to a program when it is executed. In Fortran, command-line arguments are accessed through intrinsic subroutines provided by the Fortran 2003 standard.
Fortran provides several intrinsic procedures for accessing command-line arguments:
| Procedure | Description |
|---|---|
command_argument_count() |
Returns the number of command-line arguments (excluding program name) |
get_command_argument(i, arg) |
Retrieves the i-th argument into the character variable arg |
get_command(cmd) |
Retrieves the entire command line |
program cmdargs
implicit none
integer :: num_args, i
character(len=256) :: arg
! Get the number of command-line arguments
num_args = command_argument_count()
print *, "Number of arguments: ", num_args
! Loop through and print each argument
do i = 0, num_args
call get_command_argument(i, arg)
print '(A, I0, A, A)', "args(", i, ") = ", trim(arg)
end do
end program cmdargs
Compile and run with arguments:
gfortran cmdargs.f90 -o cmdargs
./cmdargs hello world 123
Output:
command_argument_count() function returns only the count of user arguments (excluding the program name).
Here's an example that expects a filename as a command-line argument:
program process_args
implicit none
integer :: num_args, count, ios
character(len=256) :: filename, count_str
num_args = command_argument_count()
! Check if correct number of arguments provided
if (num_args < 1) then
print *, "Usage: ./process_args <filename> [count]"
stop
end if
! Get the filename (first argument)
call get_command_argument(1, filename)
print *, "Processing file: ", trim(filename)
! If a second argument is provided, treat it as a number
if (num_args >= 2) then
call get_command_argument(2, count_str)
read(count_str, *, iostat=ios) count
if (ios == 0) then
print *, "Count parameter: ", count
else
print *, "Warning: Invalid count parameter"
end if
end if
end program process_args
Fortran provides powerful file I/O capabilities through the open, read, write, and close statements.
The basic approach uses unit numbers to identify open files:
program read_file
implicit none
integer :: unit_num, ios, num_args
character(len=256) :: filename, line
num_args = command_argument_count()
if (num_args < 1) then
print *, "Usage: ./read_file <filename>"
stop
end if
call get_command_argument(1, filename)
! Open file for reading
unit_num = 10
open(unit=unit_num, file=trim(filename), status='old', &
action='read', iostat=ios)
if (ios /= 0) then
print *, "Error: Cannot open file '", trim(filename), "'"
stop
end if
! Read line by line until end of file
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit ! Exit on EOF or error
print *, trim(line)
end do
! Close the file
close(unit_num)
end program read_file
| Specifier | Description | Common Values |
|---|---|---|
unit= |
Unit number for file reference | Any positive integer (avoid 5, 6) |
file= |
Name of the file | Character string |
status= |
File existence status | 'old', 'new', 'replace', 'unknown' |
action= |
Read/write permission | 'read', 'write', 'readwrite' |
iostat= |
I/O status variable | Returns 0 on success, nonzero on error |
newunit for Automatic Unit AssignmentFortran 2008 introduced newunit to automatically assign an available unit number:
program read_newunit
implicit none
integer :: unit_num, ios, num_args
character(len=256) :: filename, line
num_args = command_argument_count()
if (num_args < 1) then
print *, "Usage: ./read_newunit <filename>"
stop
end if
call get_command_argument(1, filename)
! Open with automatic unit number assignment
open(newunit=unit_num, file=trim(filename), status='old', &
action='read', iostat=ios)
if (ios /= 0) then
print *, "Error: Cannot open file"
stop
end if
print *, "Assigned unit number: ", unit_num
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit
print *, trim(line)
end do
close(unit_num)
end program read_newunit
newunit is the recommended modern approach as it avoids potential conflicts with hardcoded unit numbers.
Use the inquire statement to check file properties before opening:
program check_file
implicit none
integer :: num_args
character(len=256) :: filename
logical :: file_exists
num_args = command_argument_count()
if (num_args < 1) then
print *, "Usage: ./check_file <filename>"
stop
end if
call get_command_argument(1, filename)
! Check if file exists
inquire(file=trim(filename), exist=file_exists)
if (.not. file_exists) then
print *, "Error: File '", trim(filename), "' not found."
stop
end if
print *, "File exists: ", trim(filename)
end program check_file
Tokenization is the process of breaking a string into smaller pieces (tokens) based on delimiters. Unlike D or Python, Fortran doesn't have a built-in split() function, but we can create one or use internal reads.
For simple whitespace-delimited data, internal reads work well:
program tokenize_simple
implicit none
character(len=100) :: line
character(len=20) :: word1, word2, word3, word4, word5
line = "The quick brown fox jumps"
! Read tokens from the string
read(line, *) word1, word2, word3, word4, word5
print *, "Token 1: ", trim(word1)
print *, "Token 2: ", trim(word2)
print *, "Token 3: ", trim(word3)
print *, "Token 4: ", trim(word4)
print *, "Token 5: ", trim(word5)
end program tokenize_simple
For comma-separated data, we need to manually find and extract tokens using the index() function:
program parse_csv
implicit none
character(len=256) :: csv_line, remaining
character(len=50) :: name, major
integer :: age, comma_pos
real :: gpa
csv_line = "Alice,25,Computer Science,3.85"
! Extract name (first field)
comma_pos = index(csv_line, ",")
name = csv_line(1:comma_pos-1)
remaining = csv_line(comma_pos+1:)
! Extract age (second field)
comma_pos = index(remaining, ",")
read(remaining(1:comma_pos-1), *) age
remaining = remaining(comma_pos+1:)
! Extract major (third field)
comma_pos = index(remaining, ",")
major = remaining(1:comma_pos-1)
remaining = remaining(comma_pos+1:)
! Extract GPA (fourth field - last field)
read(remaining, *) gpa
print *, "Name: ", trim(name)
print *, "Age: ", age
print *, "Major: ", trim(major)
print '(A, F5.2)', " GPA: ", gpa
end program parse_csv
Here's a subroutine that splits a string on a delimiter:
program tokenize_demo
implicit none
character(len=256) :: line
character(len=50) :: tokens(10)
integer :: num_tokens, i
line = "apple,banana,cherry,date,elderberry"
call split_string(line, ",", tokens, num_tokens)
print *, "Number of tokens: ", num_tokens
do i = 1, num_tokens
print '(A, I0, A, A)', " Token ", i, ": ", trim(tokens(i))
end do
contains
subroutine split_string(str, delimiter, tokens, num_tokens)
character(len=*), intent(in) :: str, delimiter
character(len=*), intent(out) :: tokens(:)
integer, intent(out) :: num_tokens
character(len=256) :: remaining
integer :: pos
num_tokens = 0
remaining = trim(str)
do while (len_trim(remaining) > 0)
pos = index(remaining, delimiter)
if (pos > 0) then
num_tokens = num_tokens + 1
tokens(num_tokens) = remaining(1:pos-1)
remaining = remaining(pos+1:)
else
! Last token (no more delimiters)
num_tokens = num_tokens + 1
tokens(num_tokens) = trim(remaining)
exit
end if
end do
end subroutine split_string
end program tokenize_demo
index(string, substring) function returns the starting position of substring within string, or 0 if not found. This is essential for manual tokenization.
Combining file reading with tokenization:
program tokenize_file
implicit none
integer :: unit_num, ios, num_args, total_tokens
character(len=256) :: filename, line
character(len=50) :: tokens(100)
integer :: num_tokens, i
logical :: file_exists
num_args = command_argument_count()
if (num_args < 1) then
print *, "Usage: ./tokenize_file <filename>"
stop
end if
call get_command_argument(1, filename)
inquire(file=trim(filename), exist=file_exists)
if (.not. file_exists) then
print *, "Error: File not found"
stop
end if
open(newunit=unit_num, file=trim(filename), status='old', action='read')
total_tokens = 0
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit
! Count words (space-separated) in this line
call split_on_spaces(line, tokens, num_tokens)
total_tokens = total_tokens + num_tokens
end do
close(unit_num)
print *, "Total tokens in file: ", total_tokens
contains
subroutine split_on_spaces(str, tokens, num_tokens)
character(len=*), intent(in) :: str
character(len=*), intent(out) :: tokens(:)
integer, intent(out) :: num_tokens
integer :: i, start, length
logical :: in_word
num_tokens = 0
in_word = .false.
length = len_trim(str)
do i = 1, length
if (str(i:i) /= ' ') then
if (.not. in_word) then
in_word = .true.
start = i
end if
else
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:i-1)
in_word = .false.
end if
end if
end do
! Don't forget the last word if line doesn't end with space
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:length)
end if
end subroutine split_on_spaces
end program tokenize_file
Fortran is renowned for its powerful array handling capabilities. Arrays can be static (fixed size at compile time) or allocatable (dynamic size at runtime).
Static arrays have a fixed size determined at compile time:
program static_arrays
implicit none
! Declare static arrays
integer :: numbers(5) ! Array of 5 integers (1 to 5)
real :: matrix(3, 3) ! 3x3 matrix
character(len=20) :: names(4) ! Array of 4 strings
! Initialize with values
integer :: primes(5) = [2, 3, 5, 7, 11]
! Explicit bounds (useful for 0-based or custom indexing)
integer :: zero_based(0:4) ! Indices 0, 1, 2, 3, 4
integer :: i
! Assign values
numbers = [10, 20, 30, 40, 50]
names = ["Alice", "Bob", "Charlie", "Diana"]
print *, "Numbers array:"
do i = 1, 5
print '(A, I0, A, I0)', " numbers(", i, ") = ", numbers(i)
end do
print *, ""
print *, "Names array:"
do i = 1, 4
print '(A, I0, A, A)', " names(", i, ") = ", trim(names(i))
end do
print *, ""
print *, "Primes: ", primes
end program static_arrays
Allocatable arrays can change size at runtime:
program allocatable_arrays
implicit none
! Declare allocatable arrays
integer, allocatable :: numbers(:)
real, allocatable :: matrix(:,:)
character(len=50), allocatable :: words(:)
integer :: n, i
print *, "Enter the size of the array: "
read *, n
! Allocate memory
allocate(numbers(n))
allocate(matrix(n, n))
allocate(words(5))
! Initialize
do i = 1, n
numbers(i) = i * 10
end do
print *, "Array size: ", size(numbers)
print *, "Array contents: ", numbers
! Deallocate when done (good practice, but automatic at program end)
deallocate(numbers)
deallocate(matrix)
deallocate(words)
end program allocatable_arrays
Fortran provides many built-in operations for arrays:
program array_operations
implicit none
integer :: arr(5) = [3, 1, 4, 1, 5]
real :: reals(5) = [1.5, 2.5, 3.5, 4.5, 5.5]
! Array intrinsic functions
print *, "Array: ", arr
print *, "Size: ", size(arr)
print *, "Sum: ", sum(arr)
print *, "Product: ", product(arr)
print *, "Minimum: ", minval(arr)
print *, "Maximum: ", maxval(arr)
print *, "Location of min: ", minloc(arr)
print *, "Location of max: ", maxloc(arr)
print *, ""
print *, "Real array: ", reals
print '(A, F8.2)', " Average: ", sum(reals) / size(reals)
! Whole-array operations
print *, ""
print *, "Doubled: ", arr * 2
print *, "Plus 10: ", arr + 10
end program array_operations
| Function | Description | Example |
|---|---|---|
size(array) |
Returns total number of elements | size(arr) → 5 |
sum(array) |
Returns sum of all elements | sum(arr) → 14 |
product(array) |
Returns product of all elements | product(arr) → 60 |
minval(array) |
Returns minimum value | minval(arr) → 1 |
maxval(array) |
Returns maximum value | maxval(arr) → 5 |
minloc(array) |
Returns location of minimum | minloc(arr) → [2] |
maxloc(array) |
Returns location of maximum | maxloc(arr) → [5] |
lbound(array) |
Returns lower bound | lbound(arr) → 1 |
ubound(array) |
Returns upper bound | ubound(arr) → 5 |
Fortran supports powerful array slicing syntax:
program array_slicing
implicit none
integer :: arr(10) = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print *, "Full array: ", arr
print *, "Elements 2 to 5: ", arr(2:5)
print *, "First 3: ", arr(1:3)
print *, "Last 3: ", arr(8:10)
print *, "Every other: ", arr(1:10:2) ! Start:End:Step
print *, "Reversed: ", arr(10:1:-1)
end program array_slicing
A complete example that reads a file, tokenizes it, and stores tokens in an array:
program file_tokens
implicit none
integer :: unit_num, ios, num_args
character(len=256) :: filename, line
character(len=50), allocatable :: all_tokens(:)
character(len=50) :: line_tokens(100)
integer :: num_tokens, total_tokens, i
logical :: file_exists
num_args = command_argument_count()
if (num_args < 1) then
print *, "Usage: ./file_tokens <filename>"
stop
end if
call get_command_argument(1, filename)
inquire(file=trim(filename), exist=file_exists)
if (.not. file_exists) then
print *, "Error: File not found."
stop
end if
! First pass: count tokens
open(newunit=unit_num, file=trim(filename), status='old', action='read')
total_tokens = 0
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit
call count_words(line, num_tokens)
total_tokens = total_tokens + num_tokens
end do
close(unit_num)
! Allocate array for all tokens
allocate(all_tokens(total_tokens))
! Second pass: store tokens
open(newunit=unit_num, file=trim(filename), status='old', action='read')
total_tokens = 0
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit
call split_on_spaces(line, line_tokens, num_tokens)
do i = 1, num_tokens
total_tokens = total_tokens + 1
all_tokens(total_tokens) = line_tokens(i)
end do
end do
close(unit_num)
! Display results
print *, "=== File Token Analysis ==="
print *, "Total tokens: ", size(all_tokens)
if (size(all_tokens) > 0) then
print *, "First token: ", trim(all_tokens(1))
print *, "Last token: ", trim(all_tokens(size(all_tokens)))
end if
print *, ""
print *, "Tokens longer than 5 characters:"
do i = 1, size(all_tokens)
if (len_trim(all_tokens(i)) > 5) then
print '(A, I0, A, A, A, I0, A)', " [", i, "]: ", &
trim(all_tokens(i)), " (length: ", len_trim(all_tokens(i)), ")"
end if
end do
! Clean up
deallocate(all_tokens)
contains
subroutine count_words(str, count)
character(len=*), intent(in) :: str
integer, intent(out) :: count
integer :: i, length
logical :: in_word
count = 0
in_word = .false.
length = len_trim(str)
do i = 1, length
if (str(i:i) /= ' ') then
if (.not. in_word) then
in_word = .true.
count = count + 1
end if
else
in_word = .false.
end if
end do
end subroutine count_words
subroutine split_on_spaces(str, tokens, num_tokens)
character(len=*), intent(in) :: str
character(len=*), intent(out) :: tokens(:)
integer, intent(out) :: num_tokens
integer :: i, start, length
logical :: in_word
num_tokens = 0
in_word = .false.
length = len_trim(str)
do i = 1, length
if (str(i:i) /= ' ') then
if (.not. in_word) then
in_word = .true.
start = i
end if
else
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:i-1)
in_word = .false.
end if
end if
end do
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:length)
end if
end subroutine split_on_spaces
end program file_tokens
Unlike languages with garbage collection (like D, Java, or Python), Fortran requires explicit memory management for allocatable arrays. This gives programmers direct control but also places responsibility for proper allocation and deallocation.
Manual memory management means:
allocatedeallocateallocate and deallocate Statementsprogram memory_demo
implicit none
integer, allocatable :: numbers(:)
real, allocatable :: matrix(:,:)
integer :: n, alloc_stat
print *, "Enter array size: "
read *, n
! Allocate memory with error checking
allocate(numbers(n), stat=alloc_stat)
if (alloc_stat /= 0) then
print *, "Error: Failed to allocate memory for numbers"
stop
end if
allocate(matrix(n, n), stat=alloc_stat)
if (alloc_stat /= 0) then
print *, "Error: Failed to allocate memory for matrix"
deallocate(numbers) ! Clean up already allocated memory
stop
end if
print *, "Successfully allocated:"
print *, " numbers: ", size(numbers), " elements"
print *, " matrix: ", size(matrix, 1), " x ", size(matrix, 2)
! Use the arrays...
numbers = 0
matrix = 0.0
! Deallocate when done
deallocate(numbers)
deallocate(matrix)
print *, "Memory deallocated successfully"
end program memory_demo
Use the allocated() function to check if an array is currently allocated:
program check_allocated
implicit none
integer, allocatable :: arr(:)
print *, "Before allocation:"
print *, " allocated(arr) = ", allocated(arr)
allocate(arr(10))
print *, "After allocation:"
print *, " allocated(arr) = ", allocated(arr)
! Safe deallocation pattern
if (allocated(arr)) then
deallocate(arr)
print *, "Array deallocated"
end if
print *, "After deallocation:"
print *, " allocated(arr) = ", allocated(arr)
end program check_allocated
allocated() to check before deallocating.
A memory leak occurs when allocated memory is never deallocated:
program memory_leak_example
implicit none
integer :: i
! BAD: This causes a memory leak!
do i = 1, 1000
call leaky_subroutine() ! Allocates but never deallocates
end do
print *, "Warning: This program leaks memory!"
contains
subroutine leaky_subroutine()
integer, allocatable :: temp(:)
allocate(temp(10000))
temp = 0
! PROBLEM: temp is never deallocated!
! Memory is lost when subroutine returns
end subroutine leaky_subroutine
end program memory_leak_example
The correct version:
program no_memory_leak
implicit none
integer :: i
! GOOD: Memory is properly managed
do i = 1, 1000
call proper_subroutine()
end do
print *, "No memory leak!"
contains
subroutine proper_subroutine()
integer, allocatable :: temp(:)
allocate(temp(10000))
temp = 0
! Always deallocate before returning
deallocate(temp)
end subroutine proper_subroutine
end program no_memory_leak
| Feature | Manual (Fortran) | Garbage Collection (D, Java) |
|---|---|---|
| Memory Allocation | Explicit with allocate |
Automatic with new |
| Memory Deallocation | Explicit with deallocate |
Automatic by GC |
| Memory Leaks | Possible if deallocate forgotten |
Prevented automatically |
| Dangling Pointers | Possible after deallocate |
Prevented automatically |
| Programmer Control | Full control | Limited control |
| Performance | No GC overhead | Small GC overhead |
| Predictability | Deterministic timing | GC pauses possible |
program large_file_processor
implicit none
integer :: num_args
character(len=256) :: filename
num_args = command_argument_count()
if (num_args < 1) then
print *, "Usage: ./large_file_processor <file1> [file2] [file3] ..."
stop
end if
print *, "=== Multi-File Processor ==="
print *, ""
call process_all_files()
print *, ""
print *, "Done! All memory has been properly deallocated."
contains
subroutine process_all_files()
integer :: i, num_args
character(len=256) :: filename
logical :: file_exists
num_args = command_argument_count()
do i = 1, num_args
call get_command_argument(i, filename)
inquire(file=trim(filename), exist=file_exists)
if (file_exists) then
call analyze_file(trim(filename))
else
print *, "Skipping (not found): ", trim(filename)
end if
end do
end subroutine process_all_files
subroutine analyze_file(filename)
character(len=*), intent(in) :: filename
character(len=256) :: line
character(len=50), allocatable :: words(:)
character(len=50) :: temp_words(100)
integer :: unit_num, ios, word_count, total_words
integer :: long_word_count, num_tokens, i
print *, "Analyzing: ", filename
! First pass: count words
open(newunit=unit_num, file=filename, status='old', action='read')
total_words = 0
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit
call count_words_in_line(line, word_count)
total_words = total_words + word_count
end do
close(unit_num)
! Allocate array for all words
allocate(words(total_words))
! Second pass: store words
open(newunit=unit_num, file=filename, status='old', action='read')
total_words = 0
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit
call extract_words(line, temp_words, num_tokens)
do i = 1, num_tokens
total_words = total_words + 1
words(total_words) = temp_words(i)
end do
end do
close(unit_num)
! Analyze
long_word_count = 0
do i = 1, size(words)
if (len_trim(words(i)) > 6) then
long_word_count = long_word_count + 1
end if
end do
print *, " Words found: ", size(words)
print *, " Long words (>6 chars): ", long_word_count
! IMPORTANT: Deallocate before returning!
deallocate(words)
print *, " Memory deallocated"
print *, ""
end subroutine analyze_file
subroutine count_words_in_line(str, count)
character(len=*), intent(in) :: str
integer, intent(out) :: count
integer :: i
logical :: in_word
count = 0
in_word = .false.
do i = 1, len_trim(str)
if (str(i:i) /= ' ') then
if (.not. in_word) then
in_word = .true.
count = count + 1
end if
else
in_word = .false.
end if
end do
end subroutine count_words_in_line
subroutine extract_words(str, tokens, num_tokens)
character(len=*), intent(in) :: str
character(len=*), intent(out) :: tokens(:)
integer, intent(out) :: num_tokens
integer :: i, start, length
logical :: in_word
num_tokens = 0
in_word = .false.
length = len_trim(str)
do i = 1, length
if (str(i:i) /= ' ') then
if (.not. in_word) then
in_word = .true.
start = i
end if
else
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:i-1)
in_word = .false.
end if
end if
end do
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:length)
end if
end subroutine extract_words
end program large_file_processor
allocate with a corresponding deallocate. Consider using a consistent pattern: allocate early, use, then deallocate before returning from subroutines.
This comprehensive example combines all the concepts: command-line arguments, file reading, tokenization, arrays, and proper memory management:
program word_frequency
implicit none
integer :: num_args
character(len=256) :: filename
logical :: file_exists
num_args = command_argument_count()
if (num_args < 1) then
print *, "Word Frequency Analyzer"
print *, "Usage: ./word_frequency <filename>"
stop
end if
call get_command_argument(1, filename)
inquire(file=trim(filename), exist=file_exists)
if (.not. file_exists) then
print *, "Error: File '", trim(filename), "' not found."
stop
end if
call analyze_word_frequency(trim(filename))
contains
subroutine analyze_word_frequency(filename)
character(len=*), intent(in) :: filename
character(len=256) :: line
character(len=50), allocatable :: unique_words(:)
integer, allocatable :: word_counts(:)
character(len=50) :: temp_words(100), word
integer :: unit_num, ios, num_tokens, i, j
integer :: num_unique, total_tokens
integer :: max_count, max_idx
logical :: found
! Initial allocation for unique words (will reallocate if needed)
allocate(unique_words(1000))
allocate(word_counts(1000))
num_unique = 0
total_tokens = 0
open(newunit=unit_num, file=filename, status='old', action='read')
do
read(unit_num, '(A)', iostat=ios) line
if (ios /= 0) exit
! Convert to lowercase
call to_lowercase(line)
! Tokenize the line
call extract_words(line, temp_words, num_tokens)
do i = 1, num_tokens
word = temp_words(i)
call remove_punctuation(word)
if (len_trim(word) == 0) cycle
total_tokens = total_tokens + 1
! Check if word already exists
found = .false.
do j = 1, num_unique
if (trim(unique_words(j)) == trim(word)) then
word_counts(j) = word_counts(j) + 1
found = .true.
exit
end if
end do
! Add new word
if (.not. found) then
num_unique = num_unique + 1
unique_words(num_unique) = word
word_counts(num_unique) = 1
end if
end do
end do
close(unit_num)
! Display results
print *, "=== Word Frequency Analysis ==="
print *, "File: ", filename
print *, "Total tokens: ", total_tokens
print *, "Unique words: ", num_unique
print *, ""
print *, "Words appearing more than once:"
print *, "--------------------------------"
do i = 1, num_unique
if (word_counts(i) > 1) then
print '(A, A, A, I0)', " ", trim(unique_words(i)), " : ", word_counts(i)
end if
end do
! Find most common word
max_count = 0
max_idx = 1
do i = 1, num_unique
if (word_counts(i) > max_count) then
max_count = word_counts(i)
max_idx = i
end if
end do
print *, ""
print '(A, A, A, I0, A)', "Most common word: '", &
trim(unique_words(max_idx)), "' (", max_count, " occurrences)"
! Clean up memory
deallocate(unique_words)
deallocate(word_counts)
end subroutine analyze_word_frequency
subroutine to_lowercase(str)
character(len=*), intent(inout) :: str
integer :: i, ic
do i = 1, len_trim(str)
ic = ichar(str(i:i))
if (ic >= ichar('A') .and. ic <= ichar('Z')) then
str(i:i) = char(ic + 32)
end if
end do
end subroutine to_lowercase
subroutine remove_punctuation(str)
character(len=*), intent(inout) :: str
character(len=20) :: punct = ".,!?;:""'()-"
integer :: i, pos
do i = 1, len_trim(punct)
do
pos = index(str, punct(i:i))
if (pos == 0) exit
str = str(1:pos-1) // str(pos+1:)
end do
end do
end subroutine remove_punctuation
subroutine extract_words(str, tokens, num_tokens)
character(len=*), intent(in) :: str
character(len=*), intent(out) :: tokens(:)
integer, intent(out) :: num_tokens
integer :: i, start, length
logical :: in_word
num_tokens = 0
in_word = .false.
length = len_trim(str)
do i = 1, length
if (str(i:i) /= ' ') then
if (.not. in_word) then
in_word = .true.
start = i
end if
else
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:i-1)
in_word = .false.
end if
end if
end do
if (in_word) then
num_tokens = num_tokens + 1
tokens(num_tokens) = str(start:length)
end if
end subroutine extract_words
end program word_frequency
| Task | Fortran Syntax |
|---|---|
| Get argument count | n = command_argument_count() |
| Get argument value | call get_command_argument(i, arg) |
| Open file for reading | open(newunit=u, file=fname, status='old', action='read') |
| Read a line | read(u, '(A)', iostat=ios) line |
| Check if file exists | inquire(file=fname, exist=exists) |
| Close file | close(u) |
| Find substring position | pos = index(str, substring) |
| Declare static array | integer :: arr(10) |
| Declare allocatable array | integer, allocatable :: arr(:) |
| Allocate memory | allocate(arr(n)) |
| Deallocate memory | deallocate(arr) |
| Check if allocated | if (allocated(arr)) ... |
| Array size | size(arr) |
| Array slice | arr(2:5) or arr(1:10:2) |
Write a Fortran program that processes a CSV (Comma-Separated Values) file containing student grade data. Your program should:
StudentName,Score1,Score2,Score3Alice,85,92,78
character allocatable array for student namesreal allocatable array for each student's average scoreAlice,85,92,78
Bob,90,88,95
Charlie,72,68,75
Diana,95,98,92
index() to find comma positions and extract tokensread(score_str, *) scoretrim() to remove trailing spaces from stringsimplicit none at the start of your programSave your program as grade_analyzer.f90 and compile with:
gfortran grade_analyzer.f90 -o grade_analyzer
./grade_analyzer grades.csv