Delphi выбор файлов по маске
В этой статье мы с вами ознакомимся с основными принципами программной организации поиска файлов. Для начала определимся, зачем нам это может быть нужно. Например, вам нужно при запуске программы на выполнение просканировать определенный каталог на присутствие DOC файлов, и при наличии таковых открыть их на редактирование или напечатать. А как вам такая идея: фоновый поиск EXE файла в сети, и при обнаружении новой версии, автоматическое обновление. Многим известны программы, где можно искать файлы, правила поиска файла. Файлы можно искать как с файловых командирах (нортон, волков, дос навигатор, фар), так в любой операционной системе. В операционной системе windows диалоговое окно поиска файла вызывается "Пуск" - "Поиск" - "Файлы и папки". В открывшимся окне необходимо задать условие искомого файла (название, маска) и путь начального поиска (каталог). На других вкладках этого диалогового окна можно расширить возможности поиска по дате изменения, по содержащемуся тексту, по размеру.
Вспомним правила поиска файлов. Вы можете задать как имя искомого файла, так и его маску, если название неизвестно или необходимо найти несколько. Т.е. применяя специальный шаблон поиска, вы можете организовать условия выборки найденных файлов. Сразу оговорюсь, что поиск можно применять как к файлам, так и к каталогам. Будем их называть элементами файловой системы. В шаблон маски искомых элементов может входить: Буквы и цифры в названии и расширении. Символ * (звездочка, математический знак "умножить"), заменяющий любое количество всевозможных букв и цифр в названии или расширении. Символ ? (знак вопроса), заменяющий одну букву или цифру в названии или расширении искомого элемента.
Например, вы ищите все текстовые файлы с расширением TXT. В поле имени искомого файла вам нужно ввести "*.TXT" (пишется без кавычек) и система найдет все такие файлы в указанном диске или каталоге. Если вам надо найти все файлы с названием semen, то в поле поиска файла нужно ввести "semen.*". Если вам нужно найти элементы с третьей буквой k и с первой буквой t в расширении, то вводите "??k*.t*". Здесь знак вопроса указывает на любой символ, третьим символом по порядку идет буква k, далее название файла (каталога) может состоять из любого количества букв и цифр, указываем звездочку. В расширении первая буква t, дальше следует любое расширение.
Примечание: файлы и каталоги в операционной системе windows ищутся без учета регистра, т.е. строчние и прописные буквы не различаются.
Теперь рассмотрим программный поиск файлов с помощью языка программирования object pascal.
Вся организация цикла поиска, а именно это и есть цикл с продолжением поиска, сводится к: Задание условий поиска. Это каталог и маска искомого элемента или элементов, атрибуты элемента(ов). При задании условий поиска сразу происходит поиск первого подходящего под условие. Это функция FindFirst. Продолжение поиска следующего элемента по заданным в первом пункте условиям. Это функция FindNext и она может вызываться сколько угодно раз, пока все файлы и каталоги, удовлетворяющие условию, не будут найдены. Закрытие поиска и освобождение памяти, выделяемую системой под поиск. Команда FindClose. Функция FindFirst.
Синтаксис: FindFirst (КАТАЛОГ_ПОИСКА_И_МАСКА_ФАЙЛА, АТРИБУТЫ_ИСКОМОГО_ФАЙЛА , ПОИСКОВОЯ_ПЕРЕМЕННАЯ);
где: Каталог для поиска и маска искомого элемента - строковая величина, имеющая тип String, может, например, содержать 'c:*.*' - все элементы в корне диска С. Обратите внимание, что указывается полный путь для поиска.
Атрибуты искомого элемента это пользовательские или системные атрибуты, которые может иметь файл (каталог, метка диска). Вот их перечень: faReadOnly - Файлы "только чтение". Такой атрибут устанавливается на файлы, которые не рекомендовано изменять, удалять. Такой атрибут имеют файлы, например, записанные на компакт-дисках. faHidden - Скрытые файлы. При обычных установках браузера и командира эти файлы невидимы. faSysFile - Системные файлы. faVolumeID - Файл метки диска. Такой элемент в своем имени имеет название диска (максимум 11 символов). faDirectory - Атрибут признака каталога. faArchive - Обычный файл. По умолчанию устанавливается на заново создаваемых файлах. faAnyFile - Если установить в качестве атрибута искомых элементов, то будет произведен поиск по всем вышесказанным атрибутам.
Эти вам нужно искать только элементы, имеющие атрибут "каталог" и "скрытый", то можно применить знак математического сложения, например faDirectory + faHidden.
Поисковая переменная имеет тип TSearchRec. В нее, при успешном результате поиска, будет занесены все необходимые данные о найденном файловом элементе.
Поскольку FindFirst является функцией, то она должна сама возвращать некоторое значение. Это значение имеет тип Integer и означает результат поиска файла (код ошибки поиска). Если файл найден, то принимает нулевое значение. Функция FindNext. FindNext ( ПОИСКОВАЯ_ПЕРЕМЕННАЯ );
Эта функция продолжает поиск, заданный в функции FindNext. Возвращает значение результата поиска (нулевое в случае успешного поиска). Процедура FindClose. FindClose ( ПОИСКОВАЯ_ПЕРЕМЕННАЯ );
Закрывает поиск и освобождает память, выделенную системой под поиск.
Теперь рассмотрим пример. Допустим, нам надо найти все файлы и каталоги в каталоге DELPHI, находящийся на диске C:. В дальнейшем, вы можете самостоятельно, изменяя маску, менять условия поиска. Для формы с компонентом ListBox1 и кнопкой Button1 реакция на OnClick по кнопке: Представленный пример кода, в принципе, является основой для организации более углубленного поиска, поиска файлов по времени создания, по содержащимся словам. Если вы запустите эту программу на выполнение, то при нажатии на кнопку Button1 вы увидите в списке в первой и второй строке элементы "." и "..". Это элементы, имеющие атрибут "каталог". Первый содержит связь с корневым каталогом диска, второй содержит связь к каталогом верхнего уровня. Со вторым вы встречаетесь в дисковых командных оболочках, например нортон, когда выбираете каталог ".." и нажимаете на "ввод". Тем самым вы попадаете в каталог на уровень выше. Естественно, в нашей поисковой программе такие элементы не надо вносить в список, поэтому мы игнорируем их нахождение. Исправляем процедуру нажатия на кнопку Button1: В этом случае, при нахождении каталога с именем "." или с именем ".." программа продолжит обработку цикла поиска без вывода найденного имени элемента в компонент списка ListBox1.
Теперь рассмотрим тип TSearchRec. Он имеет в себе несколько полезных свойств: Name - название найденного каталога (файла); Size - размер файла в байтах; Attr - атрибуты каталога (файла); Time - упакованное значение времени и даты создания каталога (файла).
Все вышеперечисленные свойства мы уже рассмотрели или они понятны сразу, за исключением свойства Time. Оно имеет тип Integer и содержит в себе упакованное значение даты и времени создания файла. Распаковка производится с помощью функции FileDateToDateTime, которая в результате возвращает значение даты и времени.
Теперь добавим в нашу форму компонент DateTimePicher1 (страница Win32) и допишем несколько строк. Как вы уже заметили, мы отбираем файлы и каталоги по дате создания, начиная с указанной в компоненте DateTimePicker1.
Теперь попробуем организовать поиск файлов во всех вложенных каталогах. Это не так просто, как может показаться на первый взгляд. Нам придется вручную организовывать весь цикл входа-выхода из каталога, перебор файлов. Немного сложноватый материал, но возможно те из вас, кто уже работал с языком программирования pascal или другим, знакомы с технологией многократности и многовложенности использования одного и того же программного кода. Коротко объясню алгоритм работы такой программы. Задание начальных условий поиска, поиск первого элемента. Если найден файл, то выводим его и соответственно обрабатываем (выводим в список, открываем, удаляем и т.п.). Если найден каталог, то начинаем новую процедуру поиска. Но программный код остается прежним. Мы просто заново вызываем и входим в эту же процедуру поиска. Обрабатываем таким же образом все вложенные в этот каталог файлы и каталоги (начинаем новый поиск в обнаруженном каталоге). Если элементов во вложенном каталоге больше нет, то обработка процедуры поиска в нем завершается, и мы выходим из нее. При этом мы оказываемся в том же месте, откуда и вызвали эту процедуру. Но она была вызвана из этой же процедуры. Поэтому программа продолжает свое выполнение дальше с момента возврата.
Таким образом, сколько витков программа наматывает на так называемый клубок, столько витков она и размотает. Программа на выполнении проходит все дерево вложенных каталогов, выполняя один и тот же кусок программного кода! И при этом данные условий поиска не перепутываются, и для каждой уникальной процедуры они сохраняются.
Рассмотрим пример. Создайте новый проект. Для создания отдельной процедуры поиска нам нужно объявить ее в соответствующем разделе (создаем ее вручную, поэтому и самостоятельно объявляем).
В разделе public пишем строку: А в разделе кода программы, до слова "end." вставляем пустой каркас процедуры На форму вставляем компонент списка ListBox1, Button1, Edit1. Для компонента Edit1 свойство Text устанавливаем в "c:delphi". Обратите внимание на последний символ, знак "", присутствие которого в начальном пути поиска обязательно. Дальше процедура OnClick для кнопки Button1 выглядит следующим образом: Созданная нами вручную процедура поиска: Если вы в компоненте Edit1 в качестве начального условия поиска файлов зададите корневую папку диска, например "С:", то вы получите полный перечень всех файлов на данном диске. Обратите внимание на скорость поиска файлов и скорость работы вашей программы.
-
Сначала находится первый файл, удовлетворяющий заданной маске. Этот поиск осуществляется с помощью функции
function FindFirst(const Path: String; Attr: Integer; var F: TSearchRec): Integer;
Параметр Path задаёт адрес каталога (директории), в котором производится поиск. Он должен завершаться маской имён искомых файлов, например:
'C:\Temp\*.*', '*.txt'.
Символ '*' означает любое количество допустимых в имени файла символов. Если необходимо указать, что символ должен быть только один, то в маске используется символ '?' . Например, маска a*.txt определяет текстовые файлы с именем любой длины начинающиеся на a , а маска a?.txt ограничивает длину имени файлов двумя символами. Если в маске описан только файл, то поиск осуществляется только в текущем каталоге.
Параметр Attr содержит набор атрибутов, которые могут учитываться при отборе файлов:
- faReadOnly = $01 - файл только для чтения;
- faHidden = $02 - скрытый файл;
- faSysFile = $04 - системный файл;
- faVolumeID = $08 - метка диска;
- faDirectory = $10 - каталог (директория);
- faArchive = $20 - архивный файл;
- faAnyFile = $3F - произвольный файл.
Эти атрибуты имеют значение отдельных битов в результирующем числе Attr. Для задания искомому файлу набора атрибутов их нужно просто просуммировать:
Attr := faReadOnly + faSysFile + faHidden;
Такой набор атрибутов заставит функцию искать только скрытые системные файлы с характеристикой "только для чтения".
Результат поиска содержится в переменной F, имеющей тип TSearchRec:
type TSearchRec = record
Time: Integer;
Size: Integer;
Attr: Integer;
Name: TFileName;
ExcludeAttr: Integer;
FindHandle: THandle;
FindData: TWin32FindData;
end;
Наиболее важными среди полей этой записи являются:
- Name - имя файла;
- Size - Размер файла в байтах;
- Time - время создания файла в формате DOS.
Переменная F, в которой первая функция сохранила результат поиска, передаётся функции FindNext в качестве параметра. На основании записанной в неё информации будет продолжен поиск следующего подходящего файла.
Для примера рассмотрим простую программку, которая поизводит поиск всех файлов на диске C. В программе папка рассматривается как файл, внутрь поиск не лезет. Можно скачать архив с этой программой.
То, что мы узнали в предыдущей части урока, позволяет работать с файлами по адресу, жёстко записанному в тексте программы. Мы же хотим иметь возможность открывать любые файлы и работать с файлами по нашему выбору. Естественно, Delphi предоставляет нам такую возможность. Рассмотрим компоненты, позволяющие в работающей программе осуществлять выбор файлов. Delphi диалоги выбора файла позволяют указать програме, с каким файлом мы хотим работать.
На вкладке палитры компонентов Dialogs находятся компонент Delphi OpenDialog и компонент Delphi SaveDialog. Все Delphi диалоги, находящиеся на этой вкладке, в том числе и Delphi диалоги выбора файла, невизуальные, т.е. при переносе их на Форму в работающей программе их не видно, они видны только на этапе конструирования. Компонент Delphi OpenDialog позволяет открыть в нашей программе стандартное Windows-окно диалога открытия файла, компонент Delphi SaveDialog - окно диалога сохранения.
Delphi диалоги выбора файла сами по себе ничего не делают, а только предоставляют настройки, сделанные пользователем при выборе файла. Самый важный метод Delphi диалогов - Execute. Он срабатывает в момент нажатия кнопки "открыть" или "сохранить" в окне выбора файла. Для примера давайте введём в программу возможность выбора файла для загрузки в редактор Memo, и сохранения после редактирования.
Итак, кидаем на Форму оба Delphi диалога, текстовый редактор Memo, и три кнопки Button. В свойство Caption одной из них записываем "Открыть. ", другой - "Сохранить", третьей - "Сохранить как. "
В обработчике OnClick кнопки "Открыть. " пишем:
if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
В результате выбора файла свойство FileName компонента OpenDialog получает значение полного адреса выбранного файла, который мы и вставляем в функцию загрузки файла компонента Memo.
Всё это хорошо, но только в данном случае, когда записанное выражение записывается в одну строку. Если программа использует несколько раз выражение OpenDialog1.FileName, то писать руками устанешь. В Delphi для такого случая есть так называемый "оператор присоединения" with. Он используется для любых объектов, имеющих длинный "хвост" из свойств, которые приходится записывать многократно. Вот как он записывается:
with Объект do
begin
Свойства Объекта внутри логических скобок begin/end можно записывать непосредственно. Допускается перечислять через запятую несколько объектов. Естественно, в случае, когда внутри скобок находится один оператор, они необязательны. Перепишем фрагмент загрузки файла с использованием оператора присоединения:
with OpenDialog1, Memo1 do
if Execute then
Lines.LoadFromFile(FileName);
Запись получается более компактной.
Так как свойства компонентов OpenDialog и SaveDialog одинаковы, сохранение текста выглядит абсолютно аналогично. Создаём обработчик нажатия кнопки "Сохранить как. " и пишем:
with SaveDialog1, Memo1 do
if Execute then
begin
Lines.SaveToFile(FileName);
OpenDialog1.FileName:=FileName; // Чтобы исправленный текст не затёр источник
end;
Наконец, для кнопки "Сохранить" пишем:
Memo1.Lines.SaveToFile(OpenDialog1.FileName); // Сохраняем туда, откуда считали
(В предыдущей строчке была ошибка. Как справедливо заметил в комментариях Oraculum - OpenDialog1.FileNam e нужно писать без кавычек.)
При работе этих фрагментов можно заметить, что выбирать приходится из всех файлов в нужной директории. Удобнее видеть только, например, текстовые файлы, или другой тип файлов по нашему выбору. Для этого используются фильтры, свойство Filter в наших компонентах. Настраивается оно в Инспекторе Объектов. При выборе его можно перейти в редактор фильтров:
В колонке FilterName записываем имена фильтров, в колонке Filter - список масок файлов, разделённых точкой с запятой. Маска файла в данном случае выглядит как
* . расширение_файла ;
Теперь, пользуясь всем пройденным материалом, можно решить задачку на тему: как определить размер файла
TOpenDialog может быть сконфигурирован для удовлетворения ваших потребности. При его использовании вы будете проходить через следующие шаги:
Создание объекта диалога
Вы определяете переменную TOpenDialog, затем присваиваете ей новый объект TOpenDialog:
var
openDialog : TOpenDialog;
begin
openDialog := TOpenDialog.Create(self);
Обратите внимание, что диалог должен иметь хозяина, в данном примере текущий объект мы снабжаем - self - как самостоятельный.
Установка опций
Перед отображением диалога, вы, вероятно, конфигурируете его по своим потребностям, устанавливая свойства диалога. Вот его основные свойства: Свойство Title
Используется для установки заголовка диалога.
Свойство FileName
Выдает имя файла для открытия, заданное по умолчанию. (Иначе, поле имени файла будет пробелом). При возвращении из диалога, если пользователь нажал, OK, это свойство будет содержать (первое) выбранное имя файла, включая его полный путь (см. первый пример).
Свойство Filter
Оно позволяет отображать и выбирать только некоторые типы файлов. Текст фильтра отображается в ниспадающем поле, чуть ниже поля имени файла. Следующий пример выбирает только текстовые файлы:
openDialog.Filter := 'Text files only|*.txt';
Раскрывающийся список показывает текст, находящийся перед разделителем |. После разделителя вы определяете маску, которая выбирает файлы, которые вы хотите.
openDialog.Filter := 'Text and Word files only|*.txt;*.doc';
Выше мы разрешили отображать два различных типа файлов, отделенные ;.
openDialog.Filter := 'Text files|*.txt|Word files|*.doc';
Выше мы разрешили отображать текстовые и Word файлы как два отдельных пункта в раскрывающемся списке.
Свойство FilterIndex
Определяет, какой фильтр ниспадающего поля будет отображен первым.
Свойство InitialDir
Устанавливает начальный каталог в диалоге.
Свойство Options
Это набор TOpenOptions флажков. Они являются исчерпывающими. Ключевые значения:
ofReadOnly открывает файл доступный только для чтения
ofFileMustExist может быть открыт только существующий файл
ofAllowMultiSelect пользователь может выбрать 2 или больше файлов
Отображение диалога
Теперь мы вызываем метод TOpenDialog:
if openDialog.Execute
then .
Execute возвратит истину, если пользователь выбрал файл и нажал OK. Теперь вы можете использовать выбранный файл:
Окончание диалога
Доступ к выбранному файлу или файлам получают, используя следующие свойства:
Свойство FileName
Оно содержит полный путь плюс имя выбранного файла.
Свойство Files
Оно содержит полный путь плюс имя файла из множественного выбора файлов. Имена файлов содержатся в возвращаемом значении TStrings (см. TStringList для получения большей информации о строковых списках).
Наконец, мы должны освободить объект диалога:
AssignFile Связывает дескриптор файла с бинарным или текстовым файлом
PromptForFileName Показывает диалог, позволяющий пользователю выбрать файл
Reset Открывает текстовый файл для чтения, или двоичный файл для чтения/записи
TSaveDialog Отображает диалог для выбора имени сохраняемого файла
TStringList Содержит список переменной длины, состоящий из строк
Delphi project files
Delphi pascal files - это отобразится в начале
Диалог установлен в текущий каталог (из которого запущен выполняемый файл).
Если Вы выбираете файл, типа 'Unit1.pas' тогда он в диалоге ShowMessage отобразит:
Использую для поиска ф-ии API FindFirstFileW и компанию. Как бы лучше организовать поиск файлов по маске, допустим такой: "*.txt; *.doc". Вижу два варианта:
1. Задаю длинную поисковую строку типа "c:\temp\*.txt; c:\temp\*.doc".
2. Ищу всё строкой типа "c:\temp\*.*" и далее проверяю найденное на предмет соответствия маске.
MsGuns © ( 2007-07-29 20:18 ) [1]
Однако файловые функции есть для этого, в.ч. и для масок
← →Sdubaruhnul ( 2007-07-29 20:49 ) [2]
>Однако файловые функции есть для этого, в.ч. и для масок
Какие? Важен Unicode.
← →Sdubaruhnul ( 2007-07-30 11:22 ) [3]
Так что, никто не может посоветовать? Как будет быстрее с точки зрения поиска файлов? Не опасно ли передавать длинные строки (ведь может получится из десять путей чёрти что)?
Готовые Дельфийские функции не годятся - они все не-Unicode"ные.
← →Сергей М. © ( 2007-07-30 11:28 ) [4]
> Готовые Дельфийские функции не годятся - они все не-Unicode"ные
function FindFirst(const Path: string; Attr: Integer;
var F: TSearchRec): Integer;
F.FindHandle := FindFirstFile(PChar(Path), F.FindData);
clickmaker © ( 2007-07-30 15:15 ) [6]
> 1. Задаю длинную поисковую строку типа "c:\temp\*.txt; c:\temp\*.doc".
> 2. Ищу всё строкой типа "c:\temp\*.*" и далее проверяю найденное
> на предмет соответствия маске.
а FindFirstFile разве поймет вариант 1?
Sdubaruhnul ( 2007-07-30 16:07 ) [7]
>а FindFirstFile разве поймет вариант 1?
Поймёт. Но мне эту строку сначала надо составить, а потом FindFirstFile будет её разбирать. И так нужно составлять для каждой подпапки, если искать всё внутри каталога. Нет, чего-то я склоняюсь ко второму варианту.
← →DVM © ( 2007-07-30 16:12 ) [8]
> Нет, чего-то я склоняюсь ко второму варианту.
ИМХО 2) удобнее и понятнее. До опр. пределов, конечно.
iXT © ( 2007-07-30 17:01 ) [9]
2 + if is Dir then FindInDir
← →Двигатель внешнего сгорания ( 2007-07-31 12:33 ) [10]
> Sdubaruhnul (30.07.07 16:07) [7]
> >а FindFirstFile разве поймет вариант 1?
Откуда такая информация?
> 1. Задаю длинную поисковую строку типа "c:\temp\*.txt; c:
> \temp\*.doc".2. Ищу всё строкой типа "c:\temp\*.*" и далее
> проверяю найденное на предмет соответствия маске.
Читайте также: