уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

С выходом версии 2010 в Delphi появился ещё один модуль — IOUtils.pas облегчающий работу с файлами и директориями. Признаться я просто катострофически не люблю работу с файлами в Delphi. Незнаю почему, но всегда напрягало реализовывать поиск по маске, чтение атрибутов и т.д. и т.п. Может из-за этого и решил рассмотреть, что же такого нового и облегчающего мою жизнь приготовили разработчики из Embarcadero.

Вначале обратимся к официальной Wiki Embarcadero и посмотрим, что там пишут про IOUtils:

…IOUtils contains three static classes: TDirectory, TPath and TFile. These classes expose a number of static methods useful for I/O tasks. …

Отлично. Всего три класса, содержащие ряд классовых методов. Начнем по порядку — с TDirectory.

TDirectory — работа с директориями.

Вспомним навскидку, какие операции с директориями обычно приходится выполнять. Мне вспомнились следующие операции:
1. Проверить существование директории;
2. Проверить наличие файлов в директории;
3. Перечислить все файлы в директории;
4. Перечислить вложенные директории;
5. Проверить даты создания/изменения директории;
6. Перечислить все логические диски в системе.
В целом, все эти операции уже давным давно расписаны на форумах, всяческих FAQ по Delphi и т.д. Так что, если потребуется найти «классический» вариант выполнения операции — Google в помощь. А мы посмотрим как можно реализовать всё это с помощью IOUtils.pas.

Как проверить существование директории, создать, удалить или скопировать директорию?

Используя IOUtils.pas проверить существование директории можно следующим образом:

if TDirectory.Exists('full_path_to_dir') then
   ShowMessage('Directory Exists')
 else
   ShowMessage('Directory Not Found');

Соответственно, если директория отсутствует, то её можно создать:

TDirectory.CreateDirectory('path_to_dir');

А если директория существует, то её можно скопировать в другое место:

TDirectory.Copy('source_path','dest_path');

При этом копируется всё содержимое директории. И наконец удалить ненужную директорию:

TDirectory.Delete('path_to_dir');

Как проверить наличие файлов в директории? Как построить дерево каталогов? Поиск по маске. Использование TSearchRec.

Если необходимо получить только факт — есть или нет что-либо в директории, что можно воспользоваться следующим методом:

if TDirectory.IsEmpty(Edit1.Text) then
   ShowMessage('Directory Empty')
 else
   ShowMessage('Directory Not Empty');

При этом, если даже в директории содержится ещё одна пустая папка, то IsEmpty вернет False.
Если нам необходимо перечислить все поддиректории, то здесь в нашем распоряжении сразу несколько вариантов:
1. Простое перичисление всех директорий:

var Dirs: TStringDynArray;
...
Dirs:=TDirectory.GetDirectories('path');
...

Здесь TStringDynArray — динамический массив (array of string). Тип TStringDynArray определен в модуле Types.
2. Поиск директорий по маске
Если простого перечисления недостаточно и необходимо найти только определенные директории, то можно воспользоваться методом GetDirectories, используя маску. В следующем примере в массив попадут все поддиректории, содержищие в названии «ee»:

  Drives:=TDirectory.GetDirectories(Edit1.Text,'*ee*');

2. Поиск директорий по уровням
По умолчанию GetDirectories проводит поиск директорий только первого уровня. Как быть, если нам необходимо пройтись по всем вложенным папкам? С появлением IOUtils в Delphi все стало проще. Достаточно использовать опции поиска:

TSearchOption = (soTopDirectoryOnly, soAllDirectories);

Соответсвенно, включив soAllDirectories в GetDirectories мы получим полное дерево каталогов:

Drives:=TDirectory.GetDirectories('path_to_dir','*',TSearchOption(1));

А как быть, если по-мимо простого поиска нам необходимо как-то визуализировать процесс или, например, в процессе поиска учитывать атрибуты директории?
3. Поиск с использованием TFilterPredicate
В этом случае можно воспользоваться процедурной ссылкой:

type
   TFilterPredicate = reference to function(const Path: string;
      const SearchRec: TSearchRec): Boolean;

Как видите, в параметрах используется уже давно нам изветный тип TSearchRec, которым мы привыкли пользоваться при поиске файлов/директорий:

TSearchRec = record
    Time: Integer;
    Size: Int64;
    Attr: Integer;
    Name: TFileName;
    ExcludeAttr: Integer;
{$IFDEF MSWINDOWS}
    FindHandle: THandle  platform;
    FindData: TWin32FindData  platform;
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
    Mode: mode_t  platform;
    FindHandle: Pointer  platform;
    PathOnly: String  platform;
    Pattern: String  platform;
{$ENDIF POSIX}
  end;

Посмотрим простой пример поиска директорий с использованием TFilterPredicate. В следующем коде в результат поиска будут включены только те директории, которые будут содержить в названии подстроку ‘api’, поиск будет проводится по всему дереву каталогов:

...
function FilterPredicate(const Path: string; const SearchRec: TSearchRec):boolean;
begin
  Result:=pos('api',SearchRec.Name)>0
end;
...
Drives:=TDirectory.GetDirectories(Edit1.Text,'*',TSearchOption(1),FilterPredicate);

Так как при использовании TFilterPredicate в нашем распоряжении имеется достаточное количество информации по текущему результату поиска, то помимо простого фильтра поиска, мы можем также подсчитать в процессе поиска размер всех файлов, запомнить какие-либо результаты и т.д. и т.п.
Всего TDirectory содержит шесть переопределенных методов GetDirectories:

class function GetDirectories(const Path: string): TStringDynArray;
        overload; inline; static;
class function GetDirectories(const Path: string;
        const Predicate: TFilterPredicate): TStringDynArray;
        overload; inline; static;
class function GetDirectories(const Path,
        SearchPattern: string): TStringDynArray; overload; inline; static;
class function GetDirectories(const Path, SearchPattern: string;
        const Predicate: TFilterPredicate): TStringDynArray;
        overload; inline; static;
class function GetDirectories(const Path, SearchPattern: string;
        const SearchOption: TSearchOption): TStringDynArray;   overload; static;
class function GetDirectories(const Path, SearchPattern: string;
        const SearchOption: TSearchOption;
        const Predicate: TFilterPredicate): TStringDynArray; overload; static;
class function GetDirectories(const Path: string;
        const SearchOption: TSearchOption;
        const Predicate: TFilterPredicate): TStringDynArray; overload; static;

Так что, для поиска и перечисления директорий возможностей у нас более, чем досаточно. Аналогичным образом проводится и поиск всех файлов в директории. Поиск файлов проводится с использованием одной из семи классовых функций GetFiles — параметры методов абсолютно идентичны тем, что используются в GetDirectories.

Как получить даты создания, последнего доступа и записи в директорию?

Получим даты создания, последнего доступа и последней записи в директории. Для этого можно воспользоваться следующими методами:

var Date:TDateTime;
Date:=TDirectory.GetCreationTime('path');//дата создания
Date:=TDirectory.GetLastAccessTime('path');//дата последнего доступа
Date:=TDirectory.GetLastWriteTime('path');//дата последнего изменения

Как перечислить все логические диски в системе?

Получить все логические диски теперь можно, выполнив всего лишь одну классовую функцию:

var Drives: TStringDynArray;
...
Drives:=TDirectory.GetLogicalDrive

В результате чего мы получим массив, содержащий все логические диски в системе.
И, наконец, ещё три метода, которые могут пригодится нам при работе с директориями:

TDirectory.GetCurrentDirectory

Возвращает текущую директорию. Можно использовать, например, аналог

ExtractFilePath(Application.ExeName)

GetCurrentDirectory возвращает полный путь к директории без последнего ‘\’.
Второй метод:

TDirectory.GetDirectoryRoot('path')

Возвращает корень для директории, т.е. по сути, логический диск на котором располагается директория. Если надо получить родительскую директорию, то используется третий метод:

TDirectory.GetParent('path')

Вот пожалуй в нескольких словах обзор методов TDirectory. В целом можно сказать, что IOUtils.pas может достаточно облегчить нашу работу в плане выполнения каких-то, ставших уже рутинных для нас, операций. Например, обход дерева каталогов, поиск файлов и директорий по маске и т.д. В следующей части посмотрим, что можно сделать с помощью методов TFile.

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
12 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Александр
02/06/2010 22:05

Если введение Character и TCharacter выглядит естественно (это новый функционал), то большого смысла в новом IOUtils я не наблюдаю: переписали старый код на новый лад. Зачем? Непонятно.

balmo
balmo
20/08/2013 20:45
Ответить на  Vlad

Наткнулся только что)
На всякий случай, вставлю пять копеек: использование IOUtils вместо старого pascal-функционала облегчает переход к кросс-платформенному коду. Спустя три года введение смотрится необходимым.

P.s.: Влад, спасибо за статью. Как и всегда, приятно и полезно)

Алексей Тимохин

> переписали старый код на новый лад. Зачем? Непонятно.
С классами работать как минимум удобнее. Имхо, было бы здорово, если бы они вообще весь VCL переписали, полностью заменив старые процедуры/функции/простые типы классами. Т.е. конечно, оставив для совместимости весь старый хлам, но позволив работать только с классами как в Java или том же C#.

atruhin
atruhin
03/06/2010 06:43

А как это хозяйство работает с символическими ссылками?
Есть ли способ отличить? Главное — удаление, не лазит ли в глубь?

Алексей (Минск)
Алексей (Минск)
03/06/2010 10:13

Надо будет сравнить с подключенным sysutils & ioutils размер выходного файла

HSerg
HSerg
03/06/2010 11:01

На днях попробовал с помощью IOUtils создать временный файлик,а затем удалить. Споткнулся о множественные проверки имен файлов и длины пути (соотв. константа не актуальна на текущих target-платформах RAD 2010). Так что пока проще обходиться Jcl.

Алексей Иванов
09/12/2015 05:08

if TDirectory.IsEmpty(Caption) then
ShowMessage(‘Отсутствуют подходящие файлы’)
else
Dirs := TDirectory.GetDirectories(Caption);

Если описать TStringDynArray в Type то выдает ошибку
[DCC Error] Main.pas(98): E2010 Incompatible types: ‘Main.TStringDynArray’ and ‘IOUtils.TStringDynArray’

Дм Бед
Дм Бед
10/11/2016 22:06

Были жалобы, что в Делфи сложно найти какую-то функцию для определенных действий. Собрали в кучу всё, что касается IO. То же самое, отчасти, и в хэлперах к строкам и т. п. В общем — верной дорогой!