Runtime Revolution
 
Articles Other News

Listing and Sorting Files in Revolution

by Oliver Kenyon

 


A very common task when writing a program is to get a list of files or folders and either display it for the user to choose one, or go through each file or folder and perform some action. This basic tutorial shows some simple algorithms to list and sort files and gives some hints on the relevant Revolution commands used.


Please download a copy of the supporting stack File Lister.rev for a working example of the code that follows.


Getting a list of files or folders

Revolution has a very simple way of obtaining lists of files and folders on a computer. The two functions files() and folders() and the global property the directory are the only things you need. The basic idea, as explained in the Documentation, is to set the directory to the folder you want to work with, and then use the two above-mentioned functions to return the lists of files or folders. 


Hint: On many file systems the folders "." and ".." refer to the current folder and the parent folder respectively. It is important to make sure that these two values don't appear in the list of folders or files because they are not real folders and more importantly as you will see later, they can cause big problems in your program. A good way to safeguard yourself from this is to use the following set of functions instead of using the files or the folders.


# Filters the strings "." and ".." from a list

function filterDots pList

  local tList

  put pList into tList

  filter tList without "."

  filter tList without ".."

  return tList

end filterDots


# Returns a filtered list of files in the current directory

function filteredFiles

  return filterDots(the files)

end filteredFiles


# Returns a filtered list of folders in the current directory

function filteredFolders

  return filterDots(the folders)

end filteredFolders


# Returns a list of files in the current directory including each file's full path.

function filteredFilesWithPaths

  local tFiles, tFilesWithPaths

  put filteredFiles() into tFiles

  repeat for each line tFile in tFiles

    put the directory & slash & tFile & return after tFilesWithPaths

  end repeat

  delete the last char of tFilesWithPaths

  return tFilesWithPaths

end filteredFilesWithPaths



Using these gives the added advantage of making it easy to filter out files with certain names, example if you only want to list Revolution Stacks. In the examples that follow, the functions

filteredFiles() and filteredFolders() with be used instead of files() and folders()


Using recursion to list the files in a folder

Often simply getting the list of files in a single folder is not enough, many applications need also to include all the sub folders. This can be easily done in Revolution using what is known as recursion.

Recursion is simply the name given to the process where a handler or function calls itself.


Hint: Recursion can allow problems to be expressed very succinctly and when properly used, it can make your code shorter and easier to read. However, recursion is often less efficient than iteration (using loops instead) and can lead to cryptic code that you have difficulty understanding yourself. If, when using recursion you find that you are struggling to understand how your code works, it is probably a good idea to try simplifying the code by using loops, even if this makes the scripts longer.


The following function shows how to use recursion to list files in Revolution.


# Returns a list of files in a given folder, using recursion to 

# include files in subfolders if desired.

function listFiles pFolder, pRecurse

  local tTotalFiles, tCurrentFiles, tFolders

  

  set the directory to pFolder

  put filteredFiles() into tCurrentFiles

  if not pRecurse then return tCurrentFiles

  if tCurrentFiles is not empty then put tCurrentFiles & return after tTotalFiles

  

  put filteredFolders() into tFolders

  repeat for each line tFolder in tFolders

    put listFiles((pFolder & slash & tFolder), pRecurse) into tCurrentFiles

    if tCurrentFiles is not empty then put tCurrentFiles & return after tTotalFiles

  end repeat

  delete the last char of tTotalFiles

  

  return tTotalFiles

end listFiles



In English you can say that the the list of files in any given folder consists of the list of files in that folder plus the list of files in each sub folder, this is exactly how this algorithm works, first listing the current files, then adding the list of files for each sub folder.


Hint: Note the way that repeat for each is used to loop through each folder. This is the most efficient way in Revolution of looping through a list, and is much faster than using repeat with x=1 to the number of lines of tFolders. Remember to delete the last character of the created list after the repeat has finished!


Hint: It is very easy to change this function to list folders instead of files, or to list both folders and files. To list folders instead just change filteredFiles() for filteredFolders() and be sure to rename the function and local variables so that they make sense. To list folders you simply add the filteredFolders() onto the end of the filteredFiles().



More file listing


The following function is very similar to listFiles() above, except with a small addition, it splits the list of files in sections, where each section is the name of the folder that the files in the section come from. This is useful if the list of files is going to be displayed to the user.


# Returns a list of files with their containing folders, using recursion

# if desired to descend into sub folders.

function listFilesWithFolders pFolder, pRecurse

  local tTotalFiles, tCurrentFiles, tFolders

  

  set the directory to pFolder

  put filteredFiles() into tCurrentFiles

  if not pRecurse then return pFolder & return & "--" & return & tCurrentFiles

  if tCurrentFiles is not empty then

    put pFolder & return & "--" & return after tTotalFiles

    put tCurrentFiles & return & return after tTotalFiles

  end if

  

  put filteredFolders() into tFolders

  repeat for each line tFolder in tFolders

    put listFilesWithFolders((pFolder & slash & tFolder), pRecurse) into tCurrentFiles

    if tCurrentFiles is not empty then put tCurrentFiles & return after tTotalFiles

  end repeat

  delete the last char of tTotalFiles

  

  return tTotalFiles

end listFilesWithFolders


The following function is another extension of listFiles() that instead of listing just the name of each file, lists the complete path of the file. This function makes use of the function filteredFilesWithPaths() which is found near the beginning of this tutorial.


# Returns a list of files with the full paths

function listFilesWithPaths pFolder, pRecurse

  local tTotalFiles, tCurrentFiles, tFolders

  

  set the directory to pFolder

  put filteredFilesWithPaths() into tCurrentFiles

  if not pRecurse then return tCurrentFiles

  if tCurrentFiles is not empty then put tCurrentFiles & return after tTotalFiles

  

  put filteredFolders() into tFolders

  repeat for each line tFolder in tFolders

    put listFilesWithPaths((pFolder & slash & tFolder), pRecurse) into tCurrentFiles

    if tCurrentFiles is not empty then put tCurrentFiles & return after tTotalFiles

  end repeat

  delete the last char of tTotalFiles

  

  return tTotalFiles

end listFilesWithPaths


Listing sorted files

The return values of the files and folders functions in Revolution are already sorted alphabetically, but if you want to obtain a properly sorted list of of files and or folders, it is necessary to sort the complete list after all recursion is complete. A flexible way of doing this is shown below.


# Returns a sorted list of the files in pFolder. Again using recursion if

# required.

function listSortedFiles pFolder, pRecurse

  local tFiles

  put listFiles(pFolder, pRecurse) into tFiles

  sort lines of tFiles by listSortedFilesSortKey(each)

  return tFiles

end listSortedFiles


# Used by the listSortedFiles() function. Returns a sort key from each file's name to

# allow the sorting to be fully customized.

function listSortedFilesSortKey pFile

  # Use this value for a normal text sort

  return pFile

end listSortedFilesSortKey



This code works by iterating through the list of files, and for each file, calling the listSortedFilesSortKey() function. This function takes the file's name and returns the string

that Revolution should use to sort this file. For a standard alphabetical sort, the name of the file is returned, but there are many other options, for example you could sort on the file's complete path instead by simply changing return pFile to return the directory & slash & pFile. Other possibilities include inverse sort, numeric sort and sort by extension which can be easily implemented. For more details on sorting in Revolution you can refer to Mark Waddingham's blog article here: Sorting in Revolution.


Performing an action for each file

This is another very common task, and in fact is slightly simpler than constructing a list of files as its not necessary to worry about making sure the list is properly formatted, ie deleting return characters etc. This handler will loop through each file in a folder and call the doSomething handler for each file, giving the handler the full path of the file. 


All that is left to do is write the doSomething handler. A simple action could be just to put the name of the file out to the message box for example:


on doSomething pFile

  put pFile & return after msg

end doSomething


This will achieve similar results to listing the files except that it will be slower because text has to be drawn to the screen at each iteration.




# Use this template function to perform an action on each file in a folder

on doForEachFile pFolder, pRecurse

  set the directory to pFolder

  

  repeat for each line tFile in filteredFiles()

    doSomething (pFolder & slash & tFile)

  end repeat

  

  if pRecurse then

    repeat for each line tFolder in filteredFolders()

      doForEachFile (pFolder & slash & tFolder), pRecurse

    end repeat

  end if

end doForEachFile


Hint: Although in this tutorial it has been neglected, it is good practice always to preserve the value of the directory in each function or handler you write. The directory is a global property and changing it is a common source of bugs in programs. The easy way to eliminate all errors of this sort is simply to add the following code around everywhere you change the directory


  local tDirectory

  put the directory into tDirectory

  set the directory to pDirectory

 

  # Insert code using the new directory here


  set the directory to tDirectory

 
©2005 Runtime Revolution Ltd, 15-19 York Place, Edinburgh, Scotland, UK, EH1 3EB.
Questions? Email info@runrev.com for answers.