Printing Directory Listing in Fortran
Asked Answered
L

3

6

How can I print a list of all the files in a given directory using Fortran, limiting only to specific file types... For instance I only want to get the list of '.txt' files from a directory.

Files in directory:

  • file1.txt
  • another.dat
  • test.mp3
  • file2.txt
  • file3.txt

something like

WHILE (not_last_file AND filetype = '.txt' )

{

print FILENAME

}

I would really appreciate your help,

Thanks

Lawyer answered 28/11, 2012 at 9:37 Comment(1)
i dont know why the down vote, its not a bad question just because it doesn't have a good standards compliant answer. If you literally just want to print the list call system("ls *.txt"), where system is a common extension and obviously the call will depend on your os.Menard
P
3

In a few words, you can't. There's no intrinsic library for such operations in Fortran that helps you. How you approach this problem will also depend on the version of Fortran you are running (F77, F90, F95 etc.) which you do not state.

"On a POSIX system using recent Fortran compiler, you can use ISO_C_BINDING to create interfaces to the POSIX opendir() and readdir() functions (or readdir_r() if you need thread safety), which allow you to iterate over the directory entries."

See this post Listing the contents of a directory in Fortran or you could also look at this overview from the gfortran documentation useful.

I hope this helps.

Pasha answered 28/11, 2012 at 9:47 Comment(2)
"There is lots of information on performing this operation avalible with a simple Google." should never be an answer on SO imhoHandiwork
You are right. Let me update this answer - I think I provided enough information without that statement to be fair.Pasha
G
1

MoonKnights's answer quotes janneb's answer in the linked question, which was had been closed because the actual question was just one unclear sentence. The quoted "On a POSIX system using recent Fortran compiler, you can use ISO_C_BINDING to create interfaces to the POSIX opendir() and readdir()" is completely true, but requires some work in the C language as the dirent structure is not easily and portably translatable to Fortran and it is better to work with just an opaque pointer there.

I use this:

#include <dirent.h> 
#include <stdlib.h>
#include <string.h>

void* open_dir(const char * const dir_name){
  DIR* d;
  d = opendir(".");
  return d;
}

void close_dir(DIR *d){
  if (d) closedir(d);
}

void next_file(DIR *d, char ch[256], int* len){
  do {
    struct dirent *dir = readdir(d);
    if (dir){
      if (dir->d_type == DT_REG)
      {
        strncpy(ch, dir->d_name, 256);
        size_t slen = strlen(dir->d_name);
        if (slen > 255) {
          *len = 255;
        } else { 
          *len = (int) slen;          
        }
      } else {
        *len = -1;
      }
    } else {
      *len = 0;
    }
  }
  while (*len<0);  
}

The code assumes that NAME_MAX==255. That will be true on most systems and avoids the need to get the value of that macro in Fortran. If that is a problem, you need to get the value of NAME_MAX to Fortran somehow or declare the header of next file in some different way.

The Fortran interfaces are then

  interface
    function open_dir(dir_name) result(res) bind(C, name="open_dir")
      use iso_c_binding
      type(c_ptr) :: res
      character(kind=c_char, len=1), intent(in) :: dir_name(*)
    end function
    
    subroutine close_dir(dir) bind(C, name="close_dir")
      use iso_c_binding
      type(c_ptr), value :: dir
    end subroutine
    
    subroutine next_file(dir, file_name, name_len) bind(C, name="next_file")
      use iso_c_binding
      type(c_ptr), value :: dir
      character(kind=c_char, len=1), intent(out) :: file_name(256)
      integer(c_int), intent(out) :: name_len
    end subroutine
  end interface

and you can loop over the files in the directory as

    type(c_ptr) :: dir_ptr = c_null_ptr
    integer(c_int) :: name_len
    character(256) :: file_name

    dir_ptr = open_dir("."//c_null_char)
    
    do
      call next_file(dir_ptr, file_name, name_len)

      print *, file_name(1:name_len)       
    end do
    
    call close_dir(dir_ptr);

The C code can be easily modified if you also need to list subdirectories (an DT_DIR branch after the DT_REG one or just use ||).

Gerardogeratology answered 14/3, 2023 at 10:1 Comment(0)
P
0

Although interfacing POSIX commands works, a quick and (somewhat) dirty way to do the same is to just use system's directory listing commands through execute_command_line, redirecting output to a temporary file. Then you can just read that file to get the listing, and delete the file when it is not needed anymore. A possible Fortran implementation can be (including some error proofing):

subroutine DirList(directory, flags)
use, intrinsic :: iso_fortran_env, only: error_unit
implicit none
character(len=*), intent(in) :: directory
character(len=*), intent(in), optional :: flags
integer :: tempunit, stat
character(len=255) :: filename
call execute_command_line("ls -1"//flags//" "//directory//" > temp", exitstat=stat)
if (stat/=0) then
  write(unit=error_unit, fmt="('Error reading directory ',a)") directory
  return
end if
open(newunit=tempunit, file="temp", status="old", action="read")
do
  read(unit=tempunit, fmt="(a)", iostat=stat) filename
  if (stat < 0) exit
  print "(a)",trim(filename)
end do
close(unit=tempunit)
call execute_command_line("rm temp")
end subroutine DirList

Here, ls -1 is used, but dir -1 should work on POSIX as well. The -1 flag is used by default so that output is one string per line (this just saves the extra work to read strings till a blank space). The optional argument flags can be used to add extra flags, if needed. Such a subroutine can be used for various tasks, e.g.:

call DirList("your-directory")         ! List all files and subdirectories
call DirList("your-directory/*.txt"    ! List *.txt files only
call DirList("your-directory/*/", "d") ! List subdirectories only

It may look like a "cheap" approach, but it works and has the extra benefit it is not restricted to POSIX systems only. For example, the subroutine above should work on Windows by replacing ls -1 with dir /b as the system's command for listing files, and rm with del for deleting the temporary file. A more sophisticated version could use the pre-processor to set those system commands as appropriate, depending the operating system the code is compiled.

A function returning an allocatable vector containing the file/subdirectory names can be easily implemented by modifying the subroutine above, if needed.

Penis answered 6/9, 2024 at 11:15 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.