работать с компонентами пути становится очень удобно, да к тому же и упрощается код функций преобразования, так как при их написании не нужно уделять внимание правильному выделению подстрок из строки полного пути.
Функция преобразования абсолютного пути в относительный (от заданной в параметре curdir папки) приводится в листинге 4.20.
Листинг 4.20.
Преобразование абсолютного пути в относительный
function AbsPathToRelative(path, curdir: String): String;
var
pathElements, curElements: TStrings;
outPath: String;
i, j: Integer;
begin
if Copy(path, 1, 2) <> Copy (curdir, 1, 2) then
begin
//Папки на разных дисках
AbsPathToRelative := path;
Exit;
end;
//Получение составляющих абсолютного и текущего пути
pathElements := TStringList.Create;
GetPathElements(path, pathElements);
curElements := TStringList.Create;
GetPathElements(curdir, curElements);
//Пропускаем одинаковые папки
i := 0;
while (i < curElements.Count) and (i < pathElements.Count)
and (CompareText(curElements[i], pathElements[i]) = 0) do Inc(i);
//Добавляем небходимое количество переходов вверх для того,
//чтобы из папки curdir попасть в общую для path и curdir папку
for j := i to curElements.Count-1 do
outPath := outPath + '..\
//Заходим из папки полученной (общей) папки в папку path
for j := i to pathElements.Count – 2 do
outPath := outPath + pathElements[j] + '
//Последним добавляем имя конечной папки или файла
AbsPathToRelative := outPath + pathElements[pathElements.Count – 1];
//Списки строк больше не нужны
pathElements.Free;
curElements.Free;
end;
При преобразовании нужно учитывать, что пути, не принадлежащие одной иерархии (например, локальный и сетевой или пути, принадлежащие разным дискам, не могут быть представлены один относительно другого: у них нет общего родительского каталога.
Обратное преобразование относительного пути в абсолютный приведено в листинге 4.21. Здесь нужно отметить, что если путь папки curdir относительный, то в итоге получим также относительный путь (только относительно другой папки). Поэтому функция и называется RelativePathToRelative, а не RelativePathToAbs.
Листи нг 4.21.
Преобразование относительного пути в абсолютный
function RelativePathToRelative(path, curdir: String): String;
var
pathElements, curElements: TStrings;
outPath: String;
i: Integer;
begin
//Получение списка составляющих абсолютного и текущего пути
pathElements := TStringList.Create;
GetPathElements(path, pathElements);
curElements := TStringList.Create;
GetPathElements(curdir, curElements);
//Изначально находимся в последней папке пути curdir
//'Путешествуем' от текущей папки вверх или вниз
//по дереву каталогов
//(прибавляя или удаляя компоненты пути в список curElements)
for i := 0 to pathElements.Count–1 do
begin
if pathElements[i] = '..' then
//Вверх по дереву
if (curElements.Count > 0)then
curElements.Delete(curElements.Count – 1)
else
curElements.Append('..')
else if pathElements[i] <> '.' then
//Вниз по дереву (знак текущей папки '.' не изменяет
//положение)
curElements.Append(pathElements[i]);
end;
//Формируем результирующий путь
if (curElements.Count > 0) then outPath := curElements[0];
for i := 1 to curElements.Count-1 do
outPath := outPath + '\' + curElements[i];
RelativePathToRelative := outPath;
//Списки строк больше не нужны
pathElements.Free;
curElements.Free;
end;
Поиск
Поиск является неотъемлемой частью работы с файловой системой. Даже простой просмотр содержимого любого каталога сопряжен с использованием простейших, но все-таки поисковых средств (перебор и, возможно, отсеивание элементов каталога). Поэтому далее мы рассмотрим возможные варианты реализации двух удобных функций поиска: поиск по маске и атрибутам файлов в пределах заданной папки и такой же поиск по всему дереву каталогов, начиная от заданной корневой папки. Все рассмотренные далее функции поиска можно найти в модуле Search, расположенном на диске, в папке с названием подраздела.
Но сначала немного сведений о масках для поиска и атрибутах файлов (и папок).
Маски и атрибуты
Маска имени файла или папки представляет собой строку, в которой неизвестный одиночный символ можно менять на? а произвольное количество (0 и более) неизвестных заранее символов – на *. Остальные (допустимые в имени) символы обозначают сами себя. Например, имена файлов SomeFile. ехе и Some. ехе удовлетворяют каждой из масок: Some* и Some*. ехе.
Атрибуты определяют некоторые важные особенности файла. Так, например, при просмотре каталога при помощи API-функций папка может отличаться от файла только наличием атрибута FILE_ATTRIBURE_DIRECTORY. Вообще содержимое папки (директории, каталога) записано на диске в самый обычный файл. Его отличает наличие указанного неизменяемого вручную атрибута и строго заданный формат записей, а также наличие специальных функций, скрывающих от нас все особенности работы с данными каталога (открытие файла, поиск нужных записей).
Итак, далее об атрибутах. Ниже приводится перечень наиболее часто используемых атрибутов файлов и каталогов (идентификаторы целочисленных констант, объявленных в модуле Windows). Если не сказано иное, атрибут можно изменить.
• FILE_ATTRIBUTE_ARCHIVE – архивный файл или каталог (на опыте замечено, что этот атрибут появляется практически у всех файлов, находящихся на диске некоторое время);
• FILE_ATTRIBUTE_DIRECTORY – атрибут каталога (атрибут нельзя самостоятельно снять или назначить);
• FILE_ATTRIBUTE_HIDDEN – скрытый файл или каталог;
• FILE_ATTRIBUTE_NORMAL – означает отсутствие особых атрибутов у файла или каталога (у последнего, естественно, всегда установлен атрибут FILE_ ATTRIBUTE_DIRECTORY);
• FILE_ATTRIBUTE_READONLY – файл или каталог только для чтения;
• FILE_ATTRIBUTE_SYSTEM – системный файл или каталог;
• FILE_ATTRIBUTE_TEMPORARY – временный файл (файловая система стремится по возможности хранить все содержимое открытого временного файла в памяти для ускорения доступа к находящимся в нем данным).
Были рассмотрены основные атрибуты, которые могут быть присвоены объектам файловой системы (файлам и папкам), но не было сказано, как получить или установить атрибуты файла или каталога. Атрибуты можно получить при просмотре содержимого каталога (как в рассмотренных далее функциях поиска). А можно использовать для этого API-функцию GetFileAttributes. Она принимает путь файла (PChar) и возвращает значение типа DWORD (32-битное целое значение), представляющее собой битовую маску. Если функция GetFileAttributes завершается неудачно, то возвращаемое значение равно $FFFFFFFF (-1 при переводе к беззнаковому целому).
Каждому из рассмотренных атрибутов соответствует бит в возвращаемом функцией GetFileAttributes значении. Вот отрывок программы, определяющей, является ли файл системным:
var attrs: DWORD;
begin
attrs := GetFileAttribute(PAnsiChar('C:oot.ini'));
if (attrs and FILE_ATTRIBUTE_SYSTEM <> 0) then {файл системный};
Атрибуты устанавливаются при помощи API-функции SetFileAttributes. Она принимает два параметра: путь файла или папки (PChar) и битовую маску атрибутов. Возвращает 0 (False) в случае неудачи и ненулевое значение в противном случае.
Поскольку в функцию SetFileAttributes передается маска, хранящая сведения сразу обо всех атрибутах файла или папки, то изменять атрибуты нужно аккуратно (чтобы не удалить установленные ранее). Пример (отрывок программы) «включения» одного и одновременного «выключения» другого атрибута файла приведен в листинге 4.22 (проверка ошибок для простоты не производится).
Листинг 4.22.
Изменение атрибутов файла
var attrs: DWORD;
begin
attrs := GetFileAttributes('C: ext.txt');
attrs := attrs or FILE_ATTRIBUTE_HIDDEN; //Установка атрибута «скрытый»
attrs := attrs and not FILE_ATTRIBUTE_ARCHIVE; //Снятие атрибута «архивный»
SetFileAttributes('C: ext.txt', attrs);
Поиск в указанной папке
Поиск в пределах одной папки представляет собой простой перебор всех элементов каталога с отбором тех, имена которых удовлетворяют маске и заданному набору атрибутов. В приведенном ниже примере (листинг4.23) используется API-функция FindFirstFile, которая начинает