Directories, Path and Filename
Get home directory of current user
Use GetUserDir
from SysUtils
unit.
- Add
SysUtils
in the uses section.
- Call
GetUserDir
.
| program GetHomeDir;
{$mode objfpc}{$H+}{$J-}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils;
var
homeDir: string;
begin
// Get home dir
homeDir := GetUserDir;
WriteLn('THe home directory is ', homeDir);
// Pause console
WriteLn('Press enter to quit ...');
ReadLn;
end.
|
Create a directory or chain of directories
Here is a snippet of creating a sub directory called demo/ex-01
in the program's current directory.
| program DirPathFileCreateDir;
{$mode objfpc}{$H+}{$J-}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes, SysUtils;
var
directoryName: string;
begin
directoryName := ConcatPaths(['demo','ex-01']);
if ForceDirectories(directoryName) then
WriteLn('Directory created successfully')
else
WriteLn('Failed to create directory');
end.
|
Create a directory or chain of directories with UTF8
You can use ForceDirectories
to create directories with UTF8.
Here is a snippet of creating a sub directory called demo/胜利
in the program's current directory.
| program DirPathFileCreateDirUTF8;
{$mode objfpc}{$H+}{$J-}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils;
var
directoryName: string;
begin
directoryName := ConcatPaths(['demo','胜利']);
if ForceDirectories(directoryName) then
WriteLn('Directory created successfully')
else
WriteLn('Failed to create directory');
end.
|
Check if a directory exists
Use FileExists
from unit SysUtils
.
| program CheckDirExists;
{$mode objfpc}{$H+}{$J-}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils;
begin
if (DirectoryExists('sub-folder/')) then
WriteLn('That folder exists!')
else
WriteLn('Can''t find it!');
// Pause console
ReadLn;
end.
|
Check if a file exists
Use FileExists
from unit SysUtils
.
| program CheckFileExists;
uses
SysUtils;
var
fileName: String;
begin
fileName := 'hello-world.txt';
if FileExists(fileName) then
Writeln( fileName, ' exists.')
else
Writeln(fileName, ' does not exist.');
end.
|
Find a file type in a folder (FindFirst)
Here is an example searching for *.csv
files using FindFirst
.
- Add
SysUtils
in uses section. Line 10.
-
Call FindFirst
with 3 arguments. Line 21.
- Path and a wildcard pattern.
- Attribute, use
faAnyFile
.
- Outpt TSearchRec variable to store results.
-
If FindFirst
returns 0, loop using repeat ... until FindNext(searchResult) <> 0
. Line 23 to 31.
- Lastly, free resources relating to
FindFirst
and FindNext
using FindClose
. Line 39.
| program FindFirstSearch;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils;
var
searchRec: TSearchRec;
path: string = './sub-folder/';
criteria: string = '*.csv';
Count: integer = 0; // Optional, only if you need a count
begin
// Call FindFirst, requires 3 arguments
if FindFirst(path + criteria, faAnyFile, searchRec) = 0 then
begin
repeat
if (searchRec.Name <> '.') and (searchRec.Name <> '..') and (searchRec.Attr <> faDirectory) then
begin
// Optional, only if you need a count -- increase a counter
Inc(Count);
// Display files found by FindFirst
WriteLn(searchRec.Name);
end;
until FindNext(searchRec) <> 0;
// A successful FindFirst call must always be followed by a FindClose call
// with the same TSearchRec record. Failure to do so will result in memory leaks.
// If the findfirst call failed (i.e. returned a nonzero handle) there is
// no need to call FindClose.
// See https://www.freepascal.org/docs-html/3.2.2/rtl/sysutils/findfirst.html
FindClose(searchRec);
end;
// Display count of matching files
WriteLn(Format('Found %d files matching %s', [Count, criteria]));
// Pause console
WriteLn;
WriteLn('Press Enter key to quit');
ReadLn;
end.
|
References
Find multiple file types in a folder (FindFirst)
See the snippet below, which looks for *.csv
and *.txt
files.
I added IsFileNameMatching
to match searchRec.Name
against a regex experssion. Line 13-34.
- When calling
FindFirst
use *
or *.*
. The Regex will do the filtering. Line 44.
- In the
repeat ... until FindNext(searchRec) <> 0
loop, simply match searchRec.Name
against a regular expression. That's it. Line 49.
- Call
FindClose
to release resources used by FindFirst
and FindNext
. Line 59.
| program FindFirstSearchRegex;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils,
RegExpr;
// A function for matching filename against a regex pattern
function IsFileNameMatching(const fileName: string; const regexPattern: string): boolean;
var
regex: TRegExpr;
begin
regex := TRegExpr.Create;
try
// Set the regex to case-insensitive
regex.ModifierI := True;
// Apply the regex pattern
regex.Expression := regexPattern;
// Check for a match
if regex.Exec(filename) then
Result := True
else
Result := False;
finally
// Free TRegExpr
regex.Free;
end;
end;
var
searchRec: TSearchRec;
path: string = './sub-folder/';
regexExpression: string = '(.csv|.txt)';
Count: integer = 0; // Optional, only if you need a count
begin
// Call FindFirst, append *.* to path
if FindFirst(path + '*.*', faAnyFile, searchRec) = 0 then
begin
repeat
if (searchRec.Name <> '.') and (searchRec.Name <> '..') and (searchRec.Attr <> faDirectory) then
begin
if IsFileNameMatching(searchRec.Name, regexExpression) then
begin
// Optional, only if you need a count -- increase a counter
Inc(Count);
// Display files found by FindFirst
WriteLn(searchRec.Name);
end;
end;
until FindNext(searchRec) <> 0;
// MUST FREE RESOURCES relating to FindFirst and FindNext
FindClose(searchRec);
end;
// Display count of matching files
WriteLn(Format('Found %d files matching %s', [Count, regexExpression]));
// Pause console
WriteLn;
WriteLn('Press Enter key to quit');
ReadLn;
end.
|
Find a file type in a folder and store in an array (FindFirst)
It is pretty straightforward.
See the snippet below that looks for *.csv
in a folder and store the names of *.csv
files in an array of string
.
- Add
SysUtils
in the uses. Line 10.
- Call the
FindFirst
. Line 22.
-
Store files found into an array in the in the repeat ... until FindNext(searchRec) <> 0
loop. Line 27-32.
- Set the new length of the array.
- Assign the
searchRec.Name
into the new index.
- Increment counter to set the new length in the next loop.
-
Lastly, free resources relating to FindFirst
and FindNext
using FindClose
. Line 36.
| program FindFirstSearchStoreArray;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils;
var
searchRec: TSearchRec;
path: string = './sub-folder/';
criteria: string = '*.csv';
filesFound: array of string;
fileCount: integer = 0;
i: integer;
begin
// Call FindFirst, append wildcard pattern to path
if FindFirst(path + criteria, faAnyFile, searchRec) = 0 then
begin
repeat
if (searchRec.Name <> '.') and (searchRec.Name <> '..') and (searchRec.Attr <> faDirectory) then
begin
// Set length the array of string
SetLength(filesFound, fileCount + 1);
// Add file name from searchRec into this array
filesFound[fileCount] := searchRec.Name;
// Increment file counter
Inc(fileCount);
end;
until FindNext(searchRec) <> 0;
// MUST RELEASE RESOURCES relating to FindFirst and FindNext
FindClose(searchRec);
end;
// Display count of matching files
WriteLn(Format('Found %d files matching %s', [Length(filesFound), criteria]));
// Display all files
for i := 0 to High(filesFound) do WriteLn(filesFound[i]);
// Pause console
WriteLn;
WriteLn('Press Enter key to quit');
ReadLn;
end.
|
Find multiple file types and store in an array (FindFirst)
Straightforwrd, simply by extending from the previous snippet we can achieve this.
- When calling
FindFirst
use *
or *.*
. The Regex will do the filtering. Line 46.
- Do
SetLength
and add searchRec.Name
into array if IsFileNameMatching(searchRec.Name, regexExpression)
returns True
. Line 53-63.
- Call
FindClose(searchRec)
at the end of FindNext(searchRec)
to avoid memory leaks. Line 67.
| program FindFirstSearchRegexStoreInArray;
{$mode objfpc}{$H+}{$J-}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils,
RegExpr;
// A function for matching filename against a regex pattern
function IsFileNameMatching(const fileName: string; const regexPattern: string): boolean;
var
regex: TRegExpr;
begin
regex := TRegExpr.Create;
try
// Set the regex to case-insensitive
regex.ModifierI := True;
// Apply the regex pattern
regex.Expression := regexPattern;
// Check for a match
if regex.Exec(filename) then
Result := True
else
Result := False;
finally
// Free TRegExpr
regex.Free;
end;
end;
var
searchRec: TSearchRec;
path: string = './sub-folder/';
regexExpression: string = '(.csv|.txt)';
filesFound:array of string;
fileCount: integer = 0;
i: integer;
begin
// Call FindFirst, append *.* to path
if FindFirst(path + '*.*', faAnyFile, searchRec) = 0 then
begin
repeat
// Skipping `.`, `..` and directories
if (searchRec.Name <> '.') and (searchRec.Name <> '..') and (searchRec.Attr <> faDirectory) then
begin
// Matching result against a regex expression
if IsFileNameMatching(searchRec.Name, regexExpression) then
begin
// Set length the array of string
SetLength(filesFound, fileCount + 1);
// Add file name from searchRec into this array
filesFound[fileCount] := searchRec.Name;
// Increment file counter
Inc(fileCount);
// Display files found by FindFirst
WriteLn(searchRec.Name);
end;
end;
until FindNext(searchRec) <> 0;
// MUST FREE RESOURCES relating to FindFirst and FindNext
FindClose(searchRec);
end;
// Display count of matching files
WriteLn(Format('Found %d files matching %s', [Length(filesFound), regexExpression]));
// Pause console
WriteLn;
WriteLn('Press Enter key to quit');
ReadLn;
end.
|
Find multiple file types recursively (FindFirst)
See an example below, using TRegExpr
and FindFirst
.
It may seem complicated, however, the algorithm in SearchFiles
is pretty straightforward.
- Call
FindFirst
using *
or *.*
. The Regex will do the filtering.
-
In the repeat ... until FindNext(searchResult) <> 0
loop;
- Check if the current
searchRec.name
it is a folder.
- If yes, call this function, along with the name of the found folder,
path + searchRec.Name
.
- If not, use
IsFileNameMatching
to match filename against a regex expression.
-
Lastly, call FindClose(searchRec)
.
| program FindFirstSearchRecursive;
{$mode objfpc}{$H+}{$J-}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
SysUtils,
RegExpr;
// A function for matching filename against a regex pattern
function IsFileNameMatching(const fileName: string; const regexPattern: string): boolean;
var
regex: TRegExpr;
begin
regex := TRegExpr.Create;
try
// Set the regex to case-insensitive
regex.ModifierI := True;
// Apply the regex pattern
regex.Expression := regexPattern;
// Check for a match
if regex.Exec(filename) then
Result := True
else
Result := False;
finally
// Free TRegExpr
regex.Free;
end;
end;
// A recursive search function using FindFirst and Regex
procedure SearchFiles(const path: string; const regexPattern: string);
var
searchRec: TSearchRec;
begin
if FindFirst(path + '*.*', faAnyFile, searchRec) = 0 then
begin
repeat
if (searchRec.Name <> '.') and (searchRec.Name <> '..') then
begin
// If searchRec.Name is a directory, then call this function recursively
if (searchRec.Attr and faDirectory) = faDirectory then
begin
// If found a directory, perform search on that directory
SearchFiles(path + searchRec.Name + PathDelim, regexPattern);
end
else
// If searchRec.Name is not a directory, check if the file matches regex pattern
begin
if IsFileNameMatching(path + searchRec.Name, regexPattern) then
// If it matches regex expression, display name
WriteLn(path + searchRec.Name);
end;
end;
until FindNext(searchRec) <> 0;
// MUST RELEASE resources relating to FindFirst and FindNext
FindClose(searchRec);
end;
end;
var
path: string = './sub-folder/';
regexPattern: string = '(.csv|.xlsx)';
begin
// Display files in a path, recursively, using a regex pattern
SearchFiles(path, regexPattern);
// Pause Console
WriteLn('Press Enter key to Exit');
ReadLn;
end.
|
Find multiple file types recursively (LazUtils package - The most straightforward of all)
You can use FindAllFiles from FileUtil
unit.
To use this unit, you must add LazUtils
package from the Project Inspector -> Required Packages
.
Here is an example. This program looks for csv
and xslx
files in a sub-folder.
- Add
FileUtil
in the uses section.
-
Invoke the FindAllFiles
and save the output into a TStringList
variable. You don't need to instantiate the TStringList
object separately; FindAllFiles
handles it automatically. When calling this function, make sure to provide the following:
- The path to be searched.
- The types of files to be searched.
- Specify whether the search should be recursive.
-
Lastly, free the TStringList
.
| program ListAllFiles;
{$mode objfpc}{$H+}{$J-}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes,
FileUtil, // Add LazUtils in Project Inspector -> Required Packages first
SysUtils;
var
searchResults: TStringList;
path: string = './sub-folder/';
criteria: string = '*.csv;*.xlsx';
isRecursive: boolean = True;
item: string;
begin
// Call FindAllFiles, no need to create TStringList manually
searchResults := FindAllFiles(path, criteria, isRecursive);
try
// Print number of files found
WriteLn(Format('Found %d files', [searchResults.Count]));
// Display results, if any
if searchResults.Count > 0 then
for item in searchResults do WriteLn(item);
finally
// Free the TStringList
searchResults.Free;
end;
// Pause console
WriteLn;
WriteLn('Press Enter key to exit ...');
ReadLn;
end.
|
References
Collect All Files from Folders and Subfolders
The follwing snippet uses the following:
Generics.Collection.TList<string>
- for storing file paths.
FindFirst
to search for files recursively.
ParamStr(n)
as files or folder to be included in the search.
program CollectFilePaths;
{$mode objfpc}{$H+}{$J-}
// Collects file paths from command line arguments
// Usage: CollectFilePaths <file|directory> [file|directory] ...
// Example: CollectFilePaths test.txt src/ libs/
uses
SysUtils, Generics.Collections;
type
// Generic list to store file paths
TFilePathList = specialize TList<string>;
// Recursively collects file paths from a directory and its subdirectories
// @param BaseDir: Directory to scan
// @param Files: List to store the found file paths
procedure CollectFilesFromDirectory(const BaseDir: string; Files: TFilePathList);
var
searchRec: TSearchRec;
findResult: Integer;
fullPath: string;
begin
// Ensure directory path ends with path separator
fullPath := IncludeTrailingPathDelimiter(BaseDir);
// Start directory scan
findResult := FindFirst(fullPath + '*.*', faAnyFile, searchRec);
try
while findResult = 0 do
begin
// Skip current and parent directory entries
if (searchRec.Name <> '.') and (searchRec.Name <> '..') then
begin
// Check if current item is a directory
if (searchRec.Attr and faDirectory) = faDirectory then
begin
// Recursively process subdirectories
CollectFilesFromDirectory(fullPath + searchRec.Name, Files);
end
else
begin
// Add file path to the list
Files.Add(fullPath + searchRec.Name);
end;
end;
findResult := FindNext(searchRec);
end;
finally
// Always close the search handle
FindClose(searchRec);
end;
end;
var
filePaths: TFilePathList;
index: integer;
path: string;
// Main Block
begin
// Create list to store all file paths
filePaths := TFilePathList.Create;
try
// Validate command line parameters
if ParamCount < 1 then
begin
WriteLn('Usage: ', ExtractFileName(ParamStr(0)), ' <file|directory> [file|directory] ...');
Exit;
end;
// Process each command line argument
for index := 1 to ParamCount do
begin
path := ParamStr(index);
// Skip invalid paths
if not FileExists(path) and not DirectoryExists(path) then
begin
WriteLn('Warning: ''', path, ''' does not exist. Skipping...');
Continue;
end;
// Handle directories and files differently
if DirectoryExists(path) then
begin
// Recursively collect all files from directory
CollectFilesFromDirectory(path, filePaths);
end
else
begin
// Add single file to list (filename only)
filePaths.Add(ExtractFileName(path));
end;
end;
// Output results
WriteLn('Collected paths:');
WriteLn('---------------');
for path in filePaths do
WriteLn(path);
WriteLn;
WriteLn('Total files found: ', filePaths.Count);
finally
// Clean up
filePaths.Free;
end;
end.