Jump to content


Photo
- - - - -

Find All Files Under Subdirectories


  • Please log in to reply
6 replies to this topic

#1 torigara

torigara

    GMC Member

  • GMC Member
  • 6483 posts

Posted 19 November 2006 - 06:50 PM

Title: Find all files under subdirectories (aka folders)
Description: Proper usage of file_find_first function, and the sample script to find all files under the specified directory.
Game Maker Version: 5.3 through 6.1, both registered and unregistered.

Finding files in a directory
To find files inside a directory, use file_find_first and file_find_next. Sounds simple? However, there are a few undocumented issues that we have to take into account.
  • Those functions only returns the file name part (as filename_name does.) To use the result with other functions, you have to append the directory name on your own.
  • If you're unsure what to put for the second argument 'attr', just put 0. Then it will find all of normal files. Note that if you specify any extra attribute, the function finds normal files plus other files those have the specified attribute. (However, specifying fa_archive has no effect because a file that has the archive attribute is considered as "normal.")
  • Don't forget to call file_find_close when you finished.
The example below searches all files which have the extension ".txt" in the current directory, and store the result in a global array, global.files.
var file_name;global.file_count = 0;file_name = file_find_first(working_directory + "\*.txt", 0);while (file_name != "") {    global.files[global.file_count] = working_directory + "\" + file_name;    global.file_count += 1;    file_name = file_find_next();}file_find_close();

Finding all files in subdirectories
To search in subdirectories, you first have to find directories with specifying fa_directory attribute, then continue searching within each of those directories. Again, there are a number of things to consider:
  • Remember that if you specify fa_directory, it finds both of normal files and directories. You have to tell whether the returned name is a file or directory (using either file_attributes or directory_exists.)
  • Every directory has two special subdirectories those don't usually show up in Windows Explorer: "." (the current directory) and ".." (the parent directory.) You have to skip those two, or it will fall into an endless loop.

The important note is that you can't use the standard recursive method here. (Because when you recursively call file_find_first during searching, Game Maker cuts off the current search and starts new search, so you can't continue searching any more when you return to the previous function.) An alternative way is as follows:
  • Every time we find a directory, put it into a queue instead of searching into it immediately.
  • When we finish searching files in one directory, we take another directory out of the aforementioned queue and start searching it.
  • Continue it until the queue comes empty.

The following script demonstrates how it can be implemented.
// This script searches all files under the specified directory.// File names are saved in a ds_list which is supplied by the user.//  argument0 = the name of the directory to start searching.//  argument1 = the extension part of the files to be found (including the period, and must be in lower case.)//  argument2 = ds_list which the file names are saved in.// Example://  file_list = ds_list_create();//  scr_find_files("C:\Windows", ".exe", file_list);var file_name, dir_name, full_path;var subdir_queue;// Put the starting position into the queue.subdir_queue = ds_queue_create();ds_queue_enqueue(subdir_queue, argument0);while (!ds_queue_empty(subdir_queue)) {    // Pop one directory out of the queue.    dir_name = ds_queue_dequeue(subdir_queue);    // Search all files and subdirectories under the directory.    file_name = file_find_first(dir_name + '\*', fa_directory);    while (file_name != '') {        full_path = dir_name + '\' + file_name;        if (file_attributes(full_path, fa_directory)) { // a directory            // Put subdirectories into the queue.            if (file_name != '.' && file_name != '..') {                ds_queue_enqueue(subdir_queue, full_path);            }        }        else { // a normal file            // Add the file to the list if it has the specified extension.            if (string_lower(filename_ext(file_name)) == argument1) {               ds_list_add(argument2, full_path);            }        }        file_name = file_find_next();    }    file_find_close();}ds_queue_destroy(subdir_queue);

Sample script for the unregistered version
The following example implements a queue with an array.
(Caution: in this one, I implemented a queue in rather lazy way, to keep the code small. It can consume a lot of memory if you have thousands of subdirectories.)
// This script searches all files under the specified directory.// File names are saved in the global array global.files,// and the number of files are saved in global.file_count.//  argument0 = the name of the directory to start searching.//  argument1 = the extension part of the files to be found (including the period, and must be in lower case.)// Example://  scr_find_files_unreg("C:\Windows", ".exe");var file_name, dir_name, full_path;var subdir_queue, queue_pos, queue_size;// Put the starting position into the queue.subdir_queue[0] = argument0;queue_pos = 0;queue_size = 1;global.file_count = 0;while (queue_pos < queue_size) {    // Pop one directory out of the queue.    dir_name = subdir_queue[queue_pos];    queue_pos += 1;    // Search all files and subdirectories under the directory.    file_name = file_find_first(dir_name + '\*', fa_directory);    while (file_name != '') {        full_path = dir_name + '\' + file_name;        if (file_attributes(full_path, fa_directory)) { // a directory            // Put subdirectories into the queue.            if (file_name != '.' && file_name != '..') {                subdir_queue[queue_size] = full_path;                queue_size += 1;            }        }        else { // a normal file            // Add the file to the list if it has the specified extension.            if (string_lower(filename_ext(file_name)) == argument1) {               global.files[global.file_count] = full_path;               global.file_count += 1;            }        }        file_name = file_find_next();    }    file_find_close();}

Edit: the important description and some of example code have been vanished for some reason (probably on transiting to the new board.) I repaired them from memory but they might not be the same as before; sorry for inconvenience.

Edited by torigara, 26 July 2010 - 02:11 AM.

  • 5

#2 Alexander_Q

Alexander_Q

    GMC Member

  • GMC Member
  • 714 posts

Posted 12 February 2008 - 07:59 AM

Torigara, your script to retrieve *.ext from all subdirectories is fantastic. I have used it, only modifying the ds_list to a ds_stack to work through the files sequentially afterwards.

Kudos.
  • 0

#3 Fede-lasse

Fede-lasse

    AI Programmer

  • GMC Member
  • 2009 posts
  • Version:Unknown

Posted 18 April 2011 - 08:39 PM

Bump for usefulness.
  • 0

#4 TheMusicDork

TheMusicDork

    GMC Member

  • GMC Member
  • 305 posts

Posted 19 April 2011 - 01:51 AM

Omg epic! Where has this been all my life=P
Thank you for 'scraping the back walls of the archives'
And many thanks to you too torigara
  • 0

#5 Recreate

Recreate

    Furry

  • GMC Member
  • 2928 posts
  • Version:GM8

Posted 30 May 2011 - 01:29 AM

Proved to be really useful for me.
  • 0

#6 Big J

Big J

    GMC Member

  • GMC Member
  • 2818 posts
  • Version:GM8.1

Posted 03 March 2012 - 02:19 AM

I know this topic is old, but... I needed a sub-directory search and this was easy to implement in my project. Thanks a lot for making this!

Edited by Big J, 03 March 2012 - 04:24 AM.

  • 0

#7 greyzebra

greyzebra

    GMC Member

  • GMC Member
  • 99 posts
  • Version:GM:Studio

Posted 05 March 2012 - 09:06 AM

Big thanks for the bump! I would never have known this otherwise
  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users