Поиск файлов
В этой статье мы с вами ознакомимся с основными принципами программной организации поиска файлов. Для начала определимся, зачем нам это может быть нужно. Например, вам нужно при запуске программы на выполнение просканировать определенный каталог на присутствие 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 по кнопке:
procedure TForm1.Button1Click(Sender: TObject); Var SR:TSearchRec; // поисковая переменная FindRes:Integer; // переменная для записи результата поиска begin ListBox1.Clear; // очистка компонента ListBox1 перед занесением в него списка файлов // задание условий поиска и начало поиска FindRes:=FindFirst('c:delphi*.*',faAnyFile,SR); While FindRes=0 do // пока мы находим файлы (каталоги), то выполнять цикл begin ListBox1.Items.Add(SR.Name); // добавление в список название найденного элемента FindRes:=FindNext(SR); // продолжение поиска по заданным условиям end; FindClose(SR); // закрываем поиск end;
Представленный пример кода, в принципе, является основой для организации более углубленного поиска, поиска файлов по времени создания, по содержащимся словам. Если вы запустите эту программу на выполнение, то при нажатии на кнопку Button1 вы увидите в списке в первой и второй строке элементы «.» и «..». Это элементы, имеющие атрибут «каталог». Первый содержит связь с корневым каталогом диска, второй содержит связь к каталогом верхнего уровня. Со вторым вы встречаетесь в дисковых командных оболочках, например нортон, когда выбираете каталог «..» и нажимаете на «ввод». Тем самым вы попадаете в каталог на уровень выше. Естественно, в нашей поисковой программе такие элементы не надо вносить в список, поэтому мы игнорируем их нахождение. Исправляем процедуру нажатия на кнопку Button1:
procedure TForm1.Button1Click(Sender: TObject); Var SR:TSearchRec; FindRes:Integer; begin ListBox1.Clear; FindRes:=FindFirst('c:delphi*.*',faAnyFile,SR); While FindRes=0 do begin // если найденный элемент каталог и if ((SR.Attr and faDirectory)=faDirectory) and // он имеет название "." или "..", тогда: ((SR.Name='.')or(SR.Name='..')) then begin FindRes:=FindNext(SR); // продолжить поиск Continue; // продолжить цикл end; ListBox1.Items.Add(SR.Name); FindRes:=FindNext(SR); end; FindClose(SR); end;
В этом случае, при нахождении каталога с именем «.» или с именем «..» программа продолжит обработку цикла поиска без вывода найденного имени элемента в компонент списка ListBox1.
Теперь рассмотрим тип TSearchRec. Он имеет в себе несколько полезных свойств:
Name — название найденного каталога (файла);
Size — размер файла в байтах;
Attr — атрибуты каталога (файла);
Time — упакованное значение времени и даты создания каталога (файла).
Все вышеперечисленные свойства мы уже рассмотрели или они понятны сразу, за исключением свойства Time. Оно имеет тип Integer и содержит в себе упакованное значение даты и времени создания файла. Распаковка производится с помощью функции FileDateToDateTime, которая в результате возвращает значение даты и времени.
Теперь добавим в нашу форму компонент DateTimePicher1 (страница Win32) и допишем несколько строк.
procedure TForm1.Button1Click(Sender: TObject); Var SR:TSearchRec; FindRes:Integer; begin ListBox1.Clear; FindRes:=FindFirst('c:delphi*.*',faAnyFile,SR); While FindRes=0 do begin if ((SR.Attr and faDirectory)=faDirectory) and ((SR.Name='.')or(SR.Name='..')) then begin FindRes:=FindNext(SR); Continue; end; // если у файла (каталога) дата создания меньше, // чем установлено в DateTimePicker1, то if FileDateToDateTime(SR.Time) begin FindRes:=FindNext(SR); // продолжить поиск Continue; // продолжить цикл end; ListBox1.Items.Add(SR.Name); FindRes:=FindNext(SR); end; FindClose(SR); end;
Как вы уже заметили, мы отбираем файлы и каталоги по дате создания, начиная с указанной в компоненте DateTimePicker1.
Теперь попробуем организовать поиск файлов во всех вложенных каталогах. Это не так просто, как может показаться на первый взгляд. Нам придется вручную организовывать весь цикл входа-выхода из каталога, перебор файлов. Немного сложноватый материал, но возможно те из вас, кто уже работал с языком программирования pascal или другим, знакомы с технологией многократности и многовложенности использования одного и того же программного кода. Коротко объясню алгоритм работы такой программы.
Задание начальных условий поиска, поиск первого элемента.
Если найден файл, то выводим его и соответственно обрабатываем (выводим в список, открываем, удаляем и т.п.).
Если найден каталог, то начинаем новую процедуру поиска. Но программный код остается прежним. Мы просто заново вызываем и входим в эту же процедуру поиска.
Обрабатываем таким же образом все вложенные в этот каталог файлы и каталоги (начинаем новый поиск в обнаруженном каталоге).
Если элементов во вложенном каталоге больше нет, то обработка процедуры поиска в нем завершается, и мы выходим из нее. При этом мы оказываемся в том же месте, откуда и вызвали эту процедуру. Но она была вызвана из этой же процедуры. Поэтому программа продолжает свое выполнение дальше с момента возврата.
Таким образом, сколько витков программа наматывает на так называемый клубок, столько витков она и размотает. Программа на выполнении проходит все дерево вложенных каталогов, выполняя один и тот же кусок программного кода! И при этом данные условий поиска не перепутываются, и для каждой уникальной процедуры они сохраняются.
Рассмотрим пример. Создайте новый проект. Для создания отдельной процедуры поиска нам нужно объявить ее в соответствующем разделе (создаем ее вручную, поэтому и самостоятельно объявляем).
В разделе public пишем строку:
procedure FindFile(Dir:String);
А в разделе кода программы, до слова «end.» вставляем пустой каркас процедуры
procedure TForm1.FindFile(Dir:String); begin end;
На форму вставляем компонент списка ListBox1, Button1, Edit1. Для компонента Edit1 свойство Text устанавливаем в «c:delphi». Обратите внимание на последний символ, знак «», присутствие которого в начальном пути поиска обязательно. Дальше процедура OnClick для кнопки Button1 выглядит следующим образом:
procedure TForm1.Button1Click(Sender: TObject); begin ListBox1.Clear; // очистка списка файлов FindFile(Edit1.Text); // поиск файлов с начальными условиями, заданных в Edit1 end;
Созданная нами вручную процедура поиска:
procedure TForm1.FindFile(Dir:String); Var SR:TSearchRec; FindRes:Integer; begin FindRes:=FindFirst(Dir+'*.*',faAnyFile,SR); While FindRes=0 do begin if ((SR.Attr and faDirectory)=faDirectory) and ((SR.Name='.')or(SR.Name='..')) then begin FindRes:=FindNext(SR); Continue; end; // если найден каталог, то if ((SR.Attr and faDirectory)=faDirectory) then begin // входим в процедуру поиска с параметрами текущего каталога + // каталог, что мы нашли FindFile(Dir+SR.Name+''); FindRes:=FindNext(SR); // после осмотра вложенного каталога мы продолжаем поиск // в этом каталоге Continue; // продолжить цикл end; ListBox1.Items.Add(SR.Name); FindRes:=FindNext(SR); end; FindClose(SR); end;
Если вы в компоненте Edit1 в качестве начального условия поиска файлов зададите корневую папку диска, например «С:», то вы получите полный перечень всех файлов на данном диске. Обратите внимание на скорость поиска файлов и скорость работы вашей программы.
Вот и всё, Удачи!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
procedure Find; var SearchRec: TSearchRec; // информация о файле или каталоге begin GetDir(0,cDir); // получить имя текущего каталога if cDir [length (cDir)] <> '' then cDir := cDir+''; //если каталог не заканчивается на '' то она добавляется в конец названия каталога if FindFirst(FileName, faAnyFile, SearchRec) = 0 then // поиск файла repeat if (SearchRec.Attr and faAnyFile) = SearchRec.Attr then begin Form1.Memo1.Lines.Add(cDir + SearchRec.Name); // добавляем файлы в Мемо n := n + 1; // эту хрень специально ввели для работы на форме end; until FindNext(SearchRec) <> 0; // до тех пор пока остается хоть 1 такой же файл // обработка подкаталогов текущего каталога if FindFirst('*.*', faDirectory, SearchRec) = 0 then repeat if (SearchRec.Attr and faDirectory) = SearchRec.Attr then begin // каталоги .. и . тоже каталоги, // но в них входить не надо if SearchRec.Name[1] <> '.' then // по моему здесь отбраываются имена с точками {Переменная SearchRec.Name — это строка типа string, а значит, мы можем к ней об- ращаться как к массиву символов. Чтобы получить доступ к первому симво- лу, мы должны написать SearchRec.Name[1]} begin ChDir(SearchRec.Name);// войти в каталог {т.к. проц. ChDir() меняет текущую папку (cDir) на указанную в скобках, то .....................................................} Find; // выполнить поиск в подкаталоге ChDir('..');// выйти из каталога end; end; until FindNext(SearchRec) <> 0; end; // возвращает каталог, выбранный пользователем function GetPath(mes: string): string; var Root: string; // корневой каталог pwRoot : PWideChar; Dir: string; begin Root := ''; {PWideChar это ссылка на структуру, (здесь структура объявлена как динамический тип) поэтому для такого указателя сначала надо выделить память. почему вводиться указатель на структуру? потому что корневой каталог в каком то смысле (может и в прямом) - структура. и чтобы представить ее как динам. тип, необх. ввести указатель. [т.е. выделяя динам. память мы уходим из стека (тем самым не давим на память компа (оперативную(?))), а пользуемся ею только когда нужно] а pwRoot - всего лишь указатель типа PWideChar. работая с ним мы только ссылаемся на структуру WideChar (а корневой каталог, как уже говорилось - это структура) можно обойтись и без указателя (и без выделения динам. памяти) (т.е. обойтись без переменной pwRoot : PWideChar;), но при этом мы занимаем бесцельно память (а так можно просто ссылаться на нужную область памяти). вид этой ф-ии без использования указателя (также Dir заменен на cDir): --------------------------------------------------------------- function GetPath(mes: string): string; begin if SelectDirectory(mes, '', cDir) then GetPath := cDir else GetPath := ''; end; -------------------------------------------------------------------- также выделить память для указателя можно и ф-ей New() вместо GetMem(var P: Pointer; Size: Integer): pwRoot:= New(PWideChar); [Если вы работаете с большими блоками данных, и структуры занимают много места в памяти, то вы должны использовать их динамические варианты, пото- му что стек не резиновый и ваша программа может рухнуть с ошибкой доступа к памяти или переполнением стека. Никогда не надейтесь на Windows и на большой объем памяти компьютера, на котором будет работать ваша програм- ма. Лучше лишний раз перестраховаться, ведь использование динамических структур не намного сложнее] } GetMem(pwRoot, (Length(Root)+1) * 2); // вообще в ф-ии GetPath непонятно *2 и MAX_PATH pwRoot := StringToWideChar(Root, pwRoot, MAX_PATH*2); if SelectDirectory(mes, pwRoot, Dir) then if length(Dir) = 2 // пользователь выбрал корневой каталог then GetPath := Dir+'' else GetPath := Dir else GetPath := ''; end; |
Рассмотрим следующий пример:
FindFirst— открывает поиск. В качестве первого параметра выступает маска поиска. Если вы укажете конкретный файл, то система найдет именно его. Вы также можете искать и целые группы файлов. Например, можно запустить поиск всех файлов в корневом каталоге диска С:. Для этого первый параметр должен быть определен как— ‘с: *. *‘. Для поиска только ЕХЕ-файлов, в папке Fold вы должны указать — ‘с: Fold * . ехе’.
Второй параметр — атрибуты, используемые при поиске файлов. Чтобы искать любые файлы, нужно указать faAnyFiie. Помимо этого, можно искать по следующим атрибутам:
- faReadOnly — искать файлы с атрибутом Readonly (только для чтения);
- faHidden — искать скрытые файлы;
- faSysFile — искать системные файлы;
- faArchive — искать архивные файлы;
- faDirectory — искать папки.
Последний параметр— это структура, в которой нам вернется информация о поиске, а именно: имя найденного файла, размер, время создания и т. д.
После вызова этой процедуры, мы должны проверить на корректность найденный файл. Если результат равен invalid_handle_value, то функция не нашла ни одного файла. Если все нормально и файл, удовлетворяющий критериям поиска, существует, то запускается цикл Repeat.. .until.
Мы уже рассматривали циклы, но все же повторимся и вспомним их работу. Цикл выполняет операторы, расположенные между repeat и until, пока условие, расположенное после слова until, выполняется. Как только условие нарушается, цикл прерывается.
Здесь надо заметить, что функция поиска возвращает через параметр Информация имена файлов и может вернуть нам в качестве имени точку или две точки. Если вы посмотрите на папку, то таких файлов не будет. Откуда берутся эти имена? Имя файла в виде точки указывает на текущую папку, а имя файла из двух точек указывает на папку верхнего уровня. Если мы встречаем такие имена, то их нужно просто отбрасывать.
Параметр Информация имеет тип структуры TSearchRec. Давайте рассмотрим ее подробнее. Объявление выглядит так:
Функция FindNext заставляет найти следующий файл, удовлетворяющий параметрам, указанным в функции FindFirst. Этой функции нужно передать структуру searchRec, по которой будет определено, на каком месте сейчас остановлен поиск, и с этого момента он будет продолжен. Как только будет найден новый файл, функция вернет в структуре SearchRec информацию о новом найденном файле.
Функция Findciose закрывает поиск. В качестве единственного параметра нужно указать все ту же структуру SearchRec.
Давайте напишем какой-нибудь реальный пример, который наглядно покажет работу с функциями поиска файлов. Посмотрите на структуру TSearchRec. Как видите, она умеет возвращать размер найденного файла. Вот и тема для примера — мы напишем код, который будет определять размер указанного файла.
Создайте новый проект и установите на форму два компонента TEdit и одну кнопку. Можете еще украсить все это текстом. У вас должно получиться нечто похожее на рис. 10.4.
По нажатии кнопки (событие onclick) напишите следующий код:
Усложним пример и попробуем написать программу, которая будет искать файл на диске, при этом сканировать и вложенные папки. Эту задачу очень удобно решать через рекурсию. Когда мы рассматривали эту тему, то реализовали абсолютно бесполезный пример, который лучше решать через простой цикл. В данном случае вы увидите реальную мощь рекурсии, закрепите знание функций поиска и увидите неплохой алгоритм.
Итак, для примера нам понадобятся на форме два поля ввода с именами:
- edLookFor — в нем будут вводить имя файла, который нужно найти;
- edLookin — диск или путь к папке, где нужно искать, включая вложенные папки. Нам также понадобятся кнопка, по нажатии которой будет происходить сканирование, и поле ввода Memo, куда будет выводиться результат, ведь файл с одним и тем же именем может быть в нескольких папках.
По нажатии кнопки пишем следующий код:
Memo1.Lines.Clear; ScanFolder(edLookin.Text);
В первой строке очищаем поле Memo от предыдущих результатов. После этого запускается функция ScanFolder, которой передаем путь, где искать файл. Код этой функции можно увидеть в листинге 10.18, а объявление ее должно быть в разделе private вашей формы в следующем виде:
В первой же строке запускаем поиск с помощью функции FindFirst. В качестве первого параметра передается папка плюс маска Vs.*’. Второй параметр равен faAnyFile, чтобы функция искала для нас все файлы любого типа. Последний параметр — это структура TSearchRec, через которую мы будем получать результат. Если файл найден, то проверяем, если имя равно точке или двум точкам, то продолжаем поиск дальше.
После этого в переменную FileName помещаем полный путь найденного файла. Для этого используем функцию SlashSep. Эта функция не существует в Delphi, мы должны сами ее написать:
Функция получает в качестве параметров путь и имя файла. Смысл заключается в том, что если путь не заканчивается слешем, то его нужно добавить. Именно это здесь и делается.
Вернемся к листингу 10.18. На следующей стадии идет проверка, что именно мы нашли — файл или папку. Если это папка, то нужно поискать файл внутри нее. Для этого мы вызываем ScanFolder, указывая вложенную папку. Но мы же уже находимся в этой функции, получается, что функция будет вызывать сама себя? Правильно — это и есть рекурсия. Мы снова вызываем ScanFolder с указанием вложенной папки, и этот вызов будет продолжаться, пока все папки не будут просканированы.
Если мы нашли не папку, то проверяем имя файла. Если оно совпадает с искомым, то выводим соответствующее сообщение в Memo компонент. Итак, поиск продолжается до тех пор, пока не переберем все файлы.
Этот пример хорош, но не эффективен. Дело в том, что мы будем перебирать абсолютно все файлы в каждой папке, что не эффективно. Намного быстрее будет искать конкретный файл сразу. Для этого первую строку изменяем следующим образом:
if FindFirst(SlashSep(Folder, sr.Name), faAnyFile, sr) = 0 then
Вот теперь в качестве первого параметра мы указываем не просто путь с маской, а конкретный файл, и результат будет положительным только в том случае, когда в искомой папке есть нужный файл.
В качестве строки поиска можно указывать и маски, например, следующая строка ищет все INI-файлы в корне диска С:
if FindFirst(' с:*.ini', faAnyFile, sr) = 0 then
Но когда вы указываете конкретный файл, то функция не будет возвращать папки. Как решить эту проблему? Попробуйте подумать сами. Есть достаточно элегантное решение.
Помоги проекту! Расскажи друзьям об этом сайте:
I hate those recursive solutions with FindFirst/FindNext and I consider it troublesome that some even forget to use FindClose to clean up resources. So, for the fun of it, a non-recursive solution that should be practical to use…
procedure FindDocs(const Root: string);
var
SearchRec: TSearchRec;
Folders: array of string;
Folder: string;
I: Integer;
Last: Integer;
begin
SetLength(Folders, 1);
Folders[0] := Root;
I := 0;
while (I < Length(Folders)) do
begin
Folder := IncludeTrailingBackslash(Folders[I]);
Inc(I);
{ Collect child folders first. }
if (FindFirst(Folder + '*.*', faDirectory, SearchRec) = 0) then
begin
repeat
if not ((SearchRec.Name = '.') or (SearchRec.Name = '..')) then
begin
Last := Length(Folders);
SetLength(Folders, Succ(Last));
Folders[Last] := Folder + SearchRec.Name;
end;
until (FindNext(SearchRec) <> 0);
FindClose(SearchRec);
end;
{ Collect files next.}
if (FindFirst(Folder + '*.doc', faAnyFile - faDirectory, SearchRec) = 0) then
begin
repeat
if not ((SearchRec.Attr and faDirectory) = faDirectory) then
begin
WriteLn(Folder, SearchRec.Name);
end;
until (FindNext(SearchRec) <> 0);
FindClose(SearchRec);
end;
end;
end;
While it seems to eat a lot of memory because it uses a dynamic array, a recursive method will do exactly the same but recursion happens on the stack! Also, with a recursive method, space is allocated for all local variables while my solution only allocates space for the folder names.
When you check for speed, both methods should be just as fast. The recursive method is easier to remember, though. You can also use a TStringList instead of a dynamic array, but I just like dynamic arrays.
One additional trick with my solution: It can search in multiple folders! I Initialized the Folders array with just one root, but you could easily set it’s length to 3, and set Folders[0] to C:, Folders[1] to D: and Folders[2] to E: and it will search on multiple disks!
Btw, replace the WriteLn() code with whatever logic you want to execute…
28 декабря 2020
Начиная с версии XE2 в Delphi появился юнит System.IOUtils, включающий удобные утилиты для работы с файлами и директориями. Предлагаю вместе подробно разобраться с тем, что в юните System.IOUtils имеется и попробовать на примерах как это всё работает.
Всего в юните System.IOUtils вы можете обнаружить три структуры TDirectory, TFile и TPath, содержащие только статические методы. Если вы разрабатываете на .NET, то вы заметите сходство этих трёх структур с классами Directory, File и Path из пространства имён System.IO в .NET. На самом деле в справке Delphi, так и написано, что большинство методов схожи с методами аналогичных классов в .NET. Это даже неплохо, ведь классы Directory, File и Path очень удобные, значит и в Delphi теперь всё должно быть удобно. Но давайте проверять.
Содержимое
-
1 Использование TDirectory
- 1.1 Создание и удаление директории
- 1.2 Проверка существования директории
- 1.3 Чтение и изменение характеристик директории
- 1.4 Копирование, перемещение и переименование директории
- 1.5 Получение списка файлов и поддиректорий в директории
- 1.6 Работа с путями директорий
- 1.7 Получение и изменение текущей директории приложения
-
2 Использование TFile
- 2.1 Создание и удаление файла
- 2.2 Функции удобной записи в файл и чтения из него
- 2.3 Ещё несколько функций для того, чтобы открыть файл
- 2.4 Проверка существования файла
- 2.5 Получение цели из символической ссылки
- 2.6 Чтение и изменение характеристик файла
- 2.7 Копирование, перемещение и переименование файлов
- 2.8 Шифрование и расшифровывание файлов
- 2.9 Замена содержимого файла
-
3 Использование TPath
- 3.1 Специальные символы
- 3.2 Получение путей к специальным и пользовательским директориям
- 3.3 Работа с путями
- 3.4 Функции для создания временных файлов
- 3.5 Проверка наличия диска
- 3.6 Функции проверки символов для использования в пути и имени файла
- 3.7 Получение атрибутов файлов и директорий
- 3.8 Заключение
Использование TDirectory
Структура TDirectory используется в Delphi для работы с директориями. Она содержит только статические методы. Давайте рассмотрим их.
Создание и удаление директории
Начнём наше знакомство со структурой TDirectory, пожалуй, с методов для создания и удаления директории. Для этого есть соответствующие методы CreateDirectory и Delete. Функция CreateDirectory принимает во входном параметре полный путь к директории и создаёт все недостающие в указанном пути папки. Например, если вы хотите создать директорию «Test3», путь к которой должен быть «C:Test1Test2Test3», а у вас на компьютере есть только директория «C:Test1», которой нет директории «Test2», то функция CreateDirectory создаст сначала директорию «Test2» в директории «Test1», а затем создаст директорию «Test3» в директории «Test2». Т.е. после вызова этой функции у вас будут все три директории, указанные в пути.
Функция Delete удаляет только последнюю директорию, указанную в пути. Например, если у вас на диске «C:» есть директория «Test1», в ней директория «Test2», и в ней директория «Test3», то чтобы удалить директорию «Test3» в функцию Delete нужно передать полный путь к этой директории, в нашем примере это будет «C:Test1Test2Test3». Также функция Delete умеет рекурсивно удалять вложенные директории и файлы, если второй входной параметр установлен в true.
Вот примеры использования функций CreateDirectory и Delete:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Создаём директории Test1, Test2 и Test3, если их нет. TDirectory.CreateDirectory(‘C:Test1Test2Test3’); //Удаляем директорию Test3. TDirectory.Delete(‘C:Test1Test2Test3’); try //Удаляем директорию Test1. //Директория Test1 не пустая, т.к. в ней есть директория Test2. //Поэтому при вызове метода Delete произойдёт ошибка EInOutError. TDirectory.Delete(‘C:Test1’); except on ioError: EInOutError do //Показываем ошибку в консоли. Writeln(ErrOutput, ioError.Message); end; //Удаляем директорию Test1 и рекурсивно все вложенные директории и файлы. TDirectory.Delete(‘C:Test1’, true); except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Проверка существования директории
Чтобы проверить, существует ли директория, у структуры TDirectory есть функция Exists. Первый параметр функции – это путь к директории. Второй необязательный параметр FollowLink учитывается только при использовании символической ссылки. Если нужно проверить только существование символической ссылки на директорию, то нужно установить этот параметр в false, а если нужно проверить наличие всего сразу, т.е. и символической ссылки на директорию и самой директории, то нужно установить этот параметр в true. Вот примеры использования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Shellapi, Classes, Windows; var execInfo: TShellExecuteInfo; begin try //Проверяем, есть ли директория C:Test1. //Если директории нет, то функция вернёт false. Writeln(TDirectory.Exists(‘C:Test1’)); //Создаём директорию. TDirectory.CreateDirectory(‘C:Test1’); //Ещё раз проверяем, есть ли директория C:Test1. //Теперь директория существует, и функция вернёт true. Writeln(TDirectory.Exists(‘C:Test1’)); //Теперь проверяем, существует ли символическая ссылка C:TestLink. //Если ссылка не существует, то функция вернёт false. Writeln(TDirectory.Exists(‘C:TestLink’, false)); //Теперь создадим символическую ссылку C:TestLink на несуществующую директорию C:Test2. //Для создания символической ссылки нужно обладать правами администратора, //поэтому создавать её будем с помощью утилиты командной строки mklink, //которую будем вызывать от имени администратора. //Чтобы вызвать эту утилиту, придётся сначала создать пакетный файл, //а затем выполнить его. //Итак, сначала создаём пакетный файл с командой mklink. with TStringList.Create do try //Содержимое пакетного файла… Add(‘mklink /D C:TestLink C:Test2’); //Сохраняем пакетный файл. SaveToFile(‘C:Test1tmp.bat’); finally Free; end; //Теперь выполняем пакетный файл от имени администратора. FillChar(execInfo, SizeOf(TShellExecuteInfo), 0); execInfo.cbSize := SizeOf(TShellExecuteInfo); execInfo.lpVerb := PWideChar(‘runas’); execInfo.lpFile := PWideChar(‘C:Test1tmp.bat’); execInfo.fMask := SEE_MASK_NOCLOSEPROCESS; ShellExecuteEx(@execInfo); //Ждём, пока пакетный файл отработает. WaitForSingleObject(execInfo.hProcess, INFINITE); //Проверяем, существует ли символическая ссылка C:TestLink. //Теперь ссылка существует, поэтому функция вернёт true. Writeln(TDirectory.Exists(‘C:TestLink’, false)); //Проверяем, существует ли символическая ссылка C:TestLink //и директория C:Test2, на которую она ссылается. //Ссылка C:TestLink существует, но директория C:Test2 — нет, //поэтому функция вернёт false. Writeln(TDirectory.Exists(‘C:TestLink’, true)); //Создаём директорию C:Test2. TDirectory.CreateDirectory(‘C:Test2’); //Опять проверяем, существует ли символическая ссылка C:TestLink //и директория C:Test2, на которую она ссылается. //Теперь ссылка C:TestLink и директория C:Test2 существуют, //поэтому функция вернёт true. Writeln(TDirectory.Exists(‘C:TestLink’, true)); //Удаляем директорию C:Test1, а заодно и файл tmp.bat. TDirectory.Delete(‘C:Test1’, true); //Удаляем директорию C:Test2. TDirectory.Delete(‘C:Test2’); //Удаляем символическую ссылку C:TestLink. DeleteFile(‘C:TestLink’); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Чтение и изменение характеристик директории
В структуре TDirectory для чтения и изменения характеристик директории есть следующие методы:
-
- GetAttributes – возвращает атрибуты папки, такие как «только чтение», «системная», «скрытая» и т.п.;
- SetAttributes – устанавливает новый набор атрибутов директории, такие как «только чтение», «системная», «скрытая» и т.п.;
- GetCreationTime и GetCreationTimeUtc – возвращают дату и время создания директории;
- SetCreationTime и SetCreationTimeUtc – устанавливают дату и время создания директории;
- GetLastAccessTime и GetLastAccessTimeUtc – возвращают дату и время последнего обращения к директории;
- SetLastAccessTime и SetLastAccessTimeUtc – устанавливают дату и время последнего обращения к директории;
- GetLastWriteTime и GetLastWriteTimeUtc – возвращают дату и время последней записи в директорию.
- SetLastWriteTime и SetLastWriteTimeUtc – устанавливают дату и время последней записи в директорию.
Функции, возвращающие и устанавливающие время, в конце имени которых есть суффикс «Utc», возвращают время в формате всемирного координированного времени (UTC).
Теперь рассмотрим примеры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Rtti; var fileAttribute: TFileAttribute; begin try //Создаём директории Test1, Test2 и Test3, если их нет. TDirectory.CreateDirectory(‘C:Test1Test2Test3’); //Делаем директорию скрытой, выставляя ей атрибут faHidden. TDirectory.SetAttributes(‘C:Test1’, [TFileAttribute.faHidden]); //Делаем директорию доступной только для чтения, выставляя ей атрибут faReadOnly. TDirectory.SetAttributes(‘C:Test1Test2Test3’, [TFileAttribute.faReadOnly]); //Отображаем все установленные атрибуты директории Test1. //В результате отобразятся два атрибута: faHidden и faDirectory. //Атрибут faDirectory убрать нельзя, он всегда будет присутствовать у директории. for fileAttribute in TDirectory.GetAttributes(‘C:Test1’) do Writeln(TRttiEnumerationType.GetName<TFileAttribute>(fileAttribute)); //Отображаем все установленные атрибуты директории Test3. //В результате отобразятся два атрибута: faReadOnly и faDirectory. for fileAttribute in TDirectory.GetAttributes(‘C:Test1Test2Test3’) do Writeln(TRttiEnumerationType.GetName<TFileAttribute>(fileAttribute)); //Выводим дату и время создания директории Test1. //В результате отобразится дата и время на момент создания директории. Writeln(DateTimeToStr(TDirectory.GetCreationTime(‘C:Test1’))); //Устанавливаем дату и время создания директории. TDirectory.SetCreationTime(‘C:Test1’, StrToDateTime(‘01.02.03 11:12:13’)); //Выводим дату и время создания директории Test1. //В результате отобразится 01.02.2003 11:12:13. Writeln(DateTimeToStr(TDirectory.GetCreationTime(‘C:Test1’))); //Выводим дату и время создания директории Test1 в формате UTC, т.е. без учёта часового пояса. //В результате отобразится 01.02.2003 8:12:13, т.е. -3 часа по Москве. Writeln(DateTimeToStr(TDirectory.GetCreationTimeUtc(‘C:Test1’))); //Выводим дату и время последнего обращения к директории. Writeln(DateTimeToStr(TDirectory.GetLastAccessTime(‘C:Test1’))); //Устанавливаем дату и время последнего обращения к директории. TDirectory.SetLastAccessTime(‘C:Test1’, StrToDateTime(‘01.02.03 11:12:14’)); //Выводим дату и время последнего обращения к директории. //В результате отобразится 01.02.2003 11:12:14. Writeln(DateTimeToStr(TDirectory.GetLastAccessTime(‘C:Test1’))); //Выводим дату и время последней записи в директорию. Writeln(DateTimeToStr(TDirectory.GetLastWriteTime(‘C:Test1’))); //Устанавливаем дату и время последней записи в директорию. TDirectory.SetLastWriteTime(‘C:Test1’, StrToDateTime(‘01.02.03 11:12:15’)); //Выводим дату и время последней записи в директорию. //В результате отобразится 01.02.2003 11:12:15. Writeln(DateTimeToStr(TDirectory.GetLastWriteTime(‘C:Test1’))); //Удаляем директорию Test1 и рекурсивно все вложенные директории и файлы. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Копирование, перемещение и переименование директории
Для копирования директории у структуры TDirectory есть метод Copy, а для перемещения или переименования – метод Move. Перечисленные выше операции производятся с директорией в целом, включая вложенные директории и файлы.
Если директория, в которую происходит копирование, уже существует, то произойдёт слияние существующей директории и директории, которую вы копируете. При этом совпадающие файлы в директории назначения будут удалены, а вместо них будут скопированы файлы из директории источника.
Вот пример использования методов Copy и Move:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Создаём директории Test1, Test2 и Test3, если их нет. TDirectory.CreateDirectory(‘C:Test1Test2Test3’); //Копируем директорию Test1 в директорию Test1.1, включая все вложенные директории и файлы. TDirectory.Copy(‘C:Test1’, ‘C:Test1.1’); //Переименовываем директорию Test1 в директорию Test1.2. TDirectory.Move(‘C:Test1’, ‘C:Test1.2’); //Перемещаем директорию Test1.2 в директорию C:UsersPublicDocuments. TDirectory.Move(‘C:Test1.2’, ‘C:UsersPublicDocumentsTest1.2’); //Удаляем директории. TDirectory.Delete(‘C:Test1.1’, true); TDirectory.Delete(‘C:UsersPublicDocumentsTest1.2’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Получение списка файлов и поддиректорий в директории
Чтобы получить список всех файлов и поддиректорий, находящихся внутри какой-либо директории у структуры TDirectory есть методы GetFiles, GetDirectories и GetFileSystemEntries. Метод GetFiles возвращает только файлы, метод GetDirectories – только поддиректории, метод GetFileSystemEntries возвращает и файлы и директории. Каждый из этих методов поддерживает шаблоны поиска и фильтры и может работать рекурсивно.
Кроме того здесь стоит упомянуть и о функции IsEmpty, которая проверяет, пустая директория или нет.
Вот примеры использования методов GetFiles, GetDirectories, GetFileSystemEntries и IsEmpty:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Types, DateUtils; var s: string; function FilterBySize(const Path: string; const SearchRec: TSearchRec): boolean; begin //Возвращаем true, если размер файла меньше 50 Кбайт. Result := SearchRec.Size < 51200; end; function FilterByDateTime(const Path: string; const SearchRec: TSearchRec): boolean; begin //Возвращаем true, если у файла или директории //дата и время последнего изменения старше 2-х лет. Result := SearchRec.TimeStamp < IncYear(Now, —2); end; begin try //Ищеи и отображаем все файлы в директории C:Windows. for s in TDirectory.GetFiles(‘C:Windows’) do Writeln(s); //Ищем и отображаем все исполняемые файлы в директории C:Windows. for s in TDirectory.GetFiles(‘C:Windows’, ‘*.exe’) do Writeln(s); //Рекурсивно ищем и отображаем все файлы //в директории C:UsersPublicPictures. for s in TDirectory.GetFiles(‘C:UsersPublicPictures’, ‘*’, TSearchOption.soAllDirectories) do Writeln(s); //Рекурсивно ищем и отображаем все файлы с расширением .jpg //в директории C:UsersPublicPictures. for s in TDirectory.GetFiles(‘C:UsersPublicPictures’, ‘*.jpg’, TSearchOption.soAllDirectories) do Writeln(s); //Рекурсивно ищем и отображаем все файлы размером меньше 50 Кбайт //в директории C:UsersPublicPictures. //Для проверки размера используется функция-фильтр FilterBySize. for s in TDirectory.GetFiles(‘C:UsersPublicPictures’, ‘*’, TSearchOption.soAllDirectories, FilterBySize) do Writeln(s); //Рекурсивно ищем и отображаем все файлы //в директории C:UsersPublicPictures, которые не изменялись более 2-х лет. //Для проверки размера используется функция-фильтр FilterByDateTime. for s in TDirectory.GetFiles(‘C:UsersPublicPictures’, ‘*’, TSearchOption.soAllDirectories, FilterByDateTime) do Writeln(s); //Ищем и отображаем все поддиректории в директории C:Windows. for s in TDirectory.GetDirectories(‘C:Windows’) do Writeln(s); //Ищем и отображаем все поддиректории, имя которых начинается с буквы s, //в директории C:Windows. for s in TDirectory.GetDirectories(‘C:Windows’, ‘s*’) do Writeln(s); //Рекурсивно ищем и отображаем все поддиректории //в директории C:UsersPublicPictures. for s in TDirectory.GetDirectories(‘C:UsersPublicPictures’, ‘*’, TSearchOption.soAllDirectories) do Writeln(s); //Рекурсивно ищем и отображаем все поддиректории //в директории C:UsersPublicPictures, которые не изменялись более 2-х лет. //Для проверки размера используется функция-фильтр FilterByDateTime. for s in TDirectory.GetDirectories(‘C:UsersPublicPictures’, ‘*’, TSearchOption.soAllDirectories, FilterByDateTime) do Writeln(s); //Ищем и отображаем все файлы и поддиректории в директории C:Windows. for s in TDirectory.GetFileSystemEntries(‘C:Windows’) do Writeln(s); //Ищем и отображаем все файлы и поддиректории, //имя которых начинается с буквы s, в директории C:Windows. for s in TDirectory.GetFileSystemEntries(‘C:Windows’, ‘s*’) do Writeln(s); //Рекурсивно ищем и отображаем все файлы и поддиректории //в директории C:UsersPublicPictures. for s in TDirectory.GetFileSystemEntries(‘C:UsersPublicPictures’, TSearchOption.soAllDirectories, nil) do Writeln(s); //Рекурсивно ищем и отображаем все файлы и поддиректории //в директории C:UsersPublicPictures, которые не изменялись более 2-х лет. //Для проверки размера используется функция-фильтр FilterByDateTime. for s in TDirectory.GetFileSystemEntries(‘C:UsersPublicPictures’, TSearchOption.soAllDirectories, FilterByDateTime) do Writeln(s); //Рекурсивно ищем и отображаем все пустые поддиректории //в директории C:UsersPublicPictures. for s in TDirectory.GetDirectories(‘C:UsersPublicPictures’, ‘*’, TSearchOption.soAllDirectories) do if TDirectory.IsEmpty(s) then Writeln(s); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Работа с путями директорий
Путь, который определяет местоположение директории, может сказать о многом. Из него, например, можно получить корневую директорию с помощью функции GetDirectoryRoot. В нём можно найти все родительские директории с помощью рекурсивного вызова функции GetParent. Иногда путь может быть относительным, что вы сможете легко проверить с помощью функции IsRelativePath.
Дополнительно может пригодиться функция GetLogicalDrives, возвращающая список всех логических дисков на компьютере.
Вот примеры использования этих функций:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; var s: string; begin try //Узнаём корневую директорию для директории C:UsersPublicPictures. //В результате мы получим ‘C:’. Writeln(TDirectory.GetDirectoryRoot(‘C:UsersPublicPictures’)); //Узнаём корневую директорию для сетевой папки \my-serverSharedMusic. //В результате мы получим ‘\my-serverShared’. Writeln(TDirectory.GetDirectoryRoot(‘\my-serverSharedMusic’)); //Узнаём корневую директорию для сетевой папки \192.168.1.100SharedDocs. //В результате мы получим ‘\192.168.1.100Shared’. Writeln(TDirectory.GetDirectoryRoot(‘\192.168.1.100SharedDocs’)); //Рекурсивно выводим все родительские директории //для директории C:UsersPublicPictures. //В результате мы получим: //’C:UsersPublic’ //’C:Users’ //’C:’ s := ‘C:UsersPublicPictures’; while not TDirectory.GetParent(s).IsEmpty do begin s := TDirectory.GetParent(s); Writeln(s); end; //Узнаём, является ли путь относительным. //В результате получим FALSE. Writeln(TDirectory.IsRelativePath(‘C:UsersPublicPictures’)); //В результате получим TRUE. Writeln(TDirectory.IsRelativePath(‘.PublicPictures’)); //В результате получим TRUE. Writeln(TDirectory.IsRelativePath(‘..SharedMusic’)); //Узнаём, какие логические диски есть на ПК. //Если у вас три диска C:, D: и F:, то результат будет таким: //’C:’ //’D:’ //’F:’ for s in TDirectory.GetLogicalDrives do Writeln(s); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Получение и изменение текущей директории приложения
Кроме всего вышеперечисленного у структуры TDirectory есть функции для получения и изменения текущей директории приложения: GetCurrentDirectory и SetCurrentDirectory. Вот примеры использования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; var s: string; begin try //Получаем тукущую директорию приложения. Writeln(TDirectory.GetCurrentDirectory); //Задаём другую текущую директорию приложения. TDirectory.SetCurrentDirectory(‘C:UsersPublicPictures’); //Ещё раз получаем тукущую директорию приложения. //Теперь это будет C:UsersPublicPictures. Writeln(TDirectory.GetCurrentDirectory); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Использование TFile
Структура TFile используется в Delphi для работы с файлами. Она содержит только статические методы. Давайте рассмотрим их.
Создание и удаление файла
Для создания файла у структуры TFile есть несколько методов:
-
- Create – создаёт пустой файл и возвращает ассоциированный с ним поток (экземпляр TFileStream), в который сразу можно записывать какие-либо данные.
- CreateText – создаёт пустой файл и возвращает экземпляр объекта TStreamWriter для записи текста в файл.
- CreateSymLink – создаёт символическую ссылку (которая, по сути, тоже является файлом) для папки или файла. Обратите внимание, что для создания символической ссылки, ваше приложение должно выполняться с правами администратора.
Для удаления файла есть функция Delete.
Рассмотрим примеры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Classes; var fileStream: TFileStream; bytes: TBytes; streamWriter: TStreamWriter; begin try //Создаём временную директорию. TDirectory.CreateDirectory(‘C:Test1’); //Создаём файл test.dat. fileStream := TFile.Create(‘C:Test1test.dat’); try bytes := [1, 2, 3]; //Записываем в файл данные из массива bytes. fileStream.Write(bytes, Length(bytes)); finally //Удаляем поток. fileStream.Free; end; //Удаляем файл test.dat. TFile.Delete(‘C:Test1test.dat’); //Создаём текстовый файл test.txt. streamWriter := TFile.CreateText(‘C:Test1test.txt’); try //Записываем текст. streamWriter.Write(‘Здравствуй, мир!’); finally //Удаляем писателя. streamWriter.Free; end; //Удаляем файл test.txt. TFile.Delete(‘C:Test1test.txt’); //Создаём символическую ссылку. if not TFile.CreateSymLink(‘C:Test1test’, ‘C:Test1test.txt’) then //Если символическая ссылка не создалась, выдаём ошибку. RaiseLastOSError; //Удаляем символическую ссылку test. TFile.Delete(‘C:Test1test’); except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; //Удаляем директорию. TDirectory.Delete(‘C:Test1’); Readln; end. |
Функции удобной записи в файл и чтения из него
У структуры TFile есть очень удобные методы, позволяющие делать запись в файл и чтение из него, всего в одну строчку. Вот эти методы:
-
- WriteAllBytes – записывает массив байт в файл. Если указанный файл существует, то он будет перезаписан.
- WriteAllLines – записывает все строки из массива строк в файл. Если указанный файл существует, то он будет перезаписан. Метод умеет конвертировать записываемый текст в нужную кодировку. Если указана кодировка и она не ANSI, то метод записывает маркер последовательности байтов (BOM).
- WriteAllText – записывает одну строку в файл. Если указанный файл существует, то он будет перезаписан. Метод умеет конвертировать записываемый текст в нужную кодировку. Также метод умеет записывать маркер последовательности байтов (BOM).
- AppendAllText – добавляет текст в конец файла. Если файла нет, то он будет создан.
- ReadAllBytes – считывает файл в новый массив байт.
- ReadAllLines – считывает все строки из файла в массив строк. Метод умеет считывать текст в заданной кодировке.
- ReadAllText – считывает весь текст из файла. Метод умеет считывать текст в заданной кодировке.
Вот примеры использования методов WriteAllText, ReadAllText и AppendAllText:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Types; begin try //Создаём директорию C:Test1. TDirectory.CreateDirectory(‘C:Test1’); //Записываем текст в файл в кодировке UTF-8 без BOM. TFile.WriteAllText(‘C:Test1test.txt’, ‘Тест!’); //Считываем текст из файла. //В результате мы увидим ‘РўРчС?С’!’, т.к. текст считался в неправильной кодировке. //Это происходит из-за того, что файл записан в кодировке UTF-8, //и т.к. BOM отсутствует, то при считывании используется кодировка ANSI. Writeln(TFile.ReadAllText(‘C:Test1test.txt’)); //Считываем текст из файла и укажем нужную кодировку. //В результате мы увидим ‘Тест!’, т.е. теперь текст считался в правильной кодировке. Writeln(TFile.ReadAllText(‘C:Test1test.txt’, TEncoding.UTF8)); //Теперь записываем текст в файл в кодировке ANSI, в которой локаль зависит //от выставленной локали на вашем компьютере. //При записи в этой кодировке, файл запишется без BOM. TFile.WriteAllText(‘C:Test1test.txt’, ‘Тест!’, TEncoding.ANSI); //Считываем текст из файла. //В результате мы увидим ‘Тест!’. В файле нет BOM, //поэтому файл считывается в кодировке ANSI, //т.е. как раз в той, в которой мы его записали. Writeln(TFile.ReadAllText(‘C:Test1test.txt’)); //Теперь записываем текст в файл в кодировке UTF-8 с BOM. TFile.WriteAllText(‘C:Test1test.txt’, ‘Тест!’, TEncoding.UTF8); //Считываем текст из файла. //В результате мы увидим ‘Тест!’. //В отличие от первого примера здесь кодировка берётся из BOM, //т.е. в нашем случае UTF-8, поэтому текст считывается корректно. Writeln(TFile.ReadAllText(‘C:Test1test.txt’)); //Добавляем в конец файла текст. //При добавлении будет использована кодировка, указанная в BOM. //Если BOM нет, то будет использована кодировка ANSI. TFile.AppendAllText(‘C:Test1test.txt’, ‘ Тест2!’); //Считываем текст из файла. //В результате мы увидим ‘Тест! Тест2!’. Writeln(TFile.ReadAllText(‘C:Test1test.txt’)); //Удаляем директорию C:Test1 и все файлы в ней. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Вот примеры использования методов WriteAllLines, ReadAllLines и AppendAllText:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Types; var s: string; begin try //Создаём директорию C:Test1. TDirectory.CreateDirectory(‘C:Test1’); //Записываем три строки в текстовый файл в кодировке UTF-8 без BOM. TFile.WriteAllLines(‘C:Test1test2.txt’, [‘Строка 1’, ‘Строка 2’, ‘Строка 3’]); //Читаем строки из файла. В результате получим: //РЎС’С?Р?РєР° 1 //РЎС’С?Р?РєР° 2 //РЎС’С?Р?РєР° 3 //Как видим строки считываются в неправильной кодировке. //Это происходит из-за того, что файл записан в кодировке UTF-8, //и т.к. BOM отсутствует, то при считывании используется кодировка ANSI. for s in TFile.ReadAllLines(‘C:Test1test2.txt’) do Writeln(s); //Теперь читаем строки из файла в нужной кодировке. Результат будет: //Строка 1 //Строка 2 //Строка 3 for s in TFile.ReadAllLines(‘C:Test1test2.txt’, TEncoding.UTF8) do Writeln(s); //Теперь записываем строки в файл в кодировке ANSI, в которой локаль зависит //от выставленной локали на вашем компьютере. //При записи в этой кодировке, файл запишется без BOM. TFile.WriteAllLines(‘C:Test1test2.txt’, [‘Строка 1’, ‘Строка 2’, ‘Строка 3’], TEncoding.ANSI); //Ещё раз считываем строки из файла. В результате получим: //Строка 1 //Строка 2 //Строка 3 //В файле нет BOM, поэтому файл считывается в кодировке ANSI, //т.е. как раз в той, в которой мы его записали. for s in TFile.ReadAllLines(‘C:Test1test2.txt’) do Writeln(s); //Теперь записываем строки в файл в кодировке UTF-8 с BOM. TFile.WriteAllLines(‘C:Test1test2.txt’, [‘Строка 1’, ‘Строка 2’, ‘Строка 3’], TEncoding.UTF8); //Считываем строки из файла. В результате увидим: //Строка 1 //Строка 2 //Строка 3 //В отличие от первого примера здесь кодировка берётся из BOM, //т.е. в нашем случае UTF-8, поэтому строки считываются корректно. for s in TFile.ReadAllLines(‘C:Test1test2.txt’) do Writeln(s); //Добавляем в конец файла ещё две строки (строки разделяются символами #13#10). //При добавлении будет использована кодировка, указанная в BOM. //Если BOM нет, то будет использована кодировка ANSI. TFile.AppendAllText(‘C:Test1test2.txt’, ‘Строка 4’#13#10’Строка 5’#13#10); //Считываем строки из файла. В результате увидим: //Строка 1 //Строка 2 //Строка 3 //Строка 4 //Строка 5 for s in TFile.ReadAllLines(‘C:Test1test2.txt’) do Writeln(s); //Удаляем директорию C:Test1 и все файлы в ней. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
А вот примеры использования функций WriteAllBytes и ReadAllBytes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Types; var b: byte; begin try //Создаём директорию C:Test1. TDirectory.CreateDirectory(‘C:Test1’); //Записываем массив из четырёх байт в файл. TFile.WriteAllBytes(‘C:Test1test.dat’, [0, 100, 200, 255]); //Считываем байты из файла. В результате увидим в консоли: //0 //100 //200 //255 for b in TFile.ReadAllBytes(‘C:Test1test.dat’) do Writeln(b); //Удаляем директорию C:Test1 и все файлы в ней. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Ещё несколько функций для того, чтобы открыть файл
Теперь рассмотрим ещё на несколько удобных функций для открытия файла. Это функции AppendText, OpenText, Open, OpenRead, OpenWrite.
Функции AppendText и OpenText предназначены для работы с текстовыми файлами в кодировке UTF-8. AppendText открывает файл, или создаёт новый файл, если его не было, для добавления в него текста, а OpenText – для чтения из текстового файла. Функция AppendText для работы с файлом, создаёт и возвращает объект TStreamWriter, который после использования нужно удалить. Функция OpenText для чтения текста создаёт и возвращает объект TStreamReader, который после использования также нужно удалить.
Функции Open, OpenRead, OpenWrite по сути просто создают объект TFileStream и возвращают его. Функция OpenRead открывает файл на чтение, функция OpenWrite — на запись, а функция Open может, как создавать файлы, так и открывать их в различных режимах и с различными привилегиями.
Рассмотрим примеры использования этих функций:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Classes; var streamWriter: TStreamWriter; streamReader: TStreamReader; fileStream: TFileStream; bytes: TBytes; b: byte; begin try //Создаём временную директорию. TDirectory.CreateDirectory(‘C:Test1’); //Создаём текстовый файл test.txt в кодировке UTF-8 и добавляем в две строки текста. streamWriter := TFile.AppendText(‘C:Test1test.txt’); try streamWriter.Write(‘Привет, мир!’); streamWriter.WriteLine; streamWriter.Write(‘Это вторая строка в тексте.’); finally streamWriter.Free; end; //Открываем текстовый файл в кодировке UTF-8 на чтение, и считываем две строки текста. streamReader := TFile.OpenText(‘C:Test1test.txt’); try Writeln(‘1-я строка: ‘ + streamReader.ReadLine); Writeln(‘2-я строка: ‘ + streamReader.ReadLine); finally streamReader.Free; end; //Создаём файл функцией Open и записываем 3 байта. fileStream := TFile.Open(‘C:Test1test.dat’, TFileMode.fmCreate, TFileAccess.faWrite); try bytes := [10, 20, 30]; fileStream.Write(bytes, 3); finally fileStream.Free; end; //Дописываем в файл ещё 3 байта функцией OpenWrite. fileStream := TFile.OpenWrite(‘C:Test1test.dat’); try bytes := [40, 50, 60]; fileStream.Seek(0, TSeekOrigin.soEnd); fileStream.Write(bytes, 3); finally fileStream.Free; end; //Читаем из файла все байты функцией OpenRead. fileStream := TFile.OpenRead(‘C:Test1test.dat’); try SetLength(bytes, fileStream.Size); fileStream.Read(bytes, fileStream.Size); for b in bytes do Writeln(b); finally fileStream.Free; end; //Удаляем директорию C:Test1 и все файлы в ней. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Проверка существования файла
Так же как и у TDirectory у структуры TFile есть функция Exists. Первый параметр функции – это путь к файлу. Второй необязательный параметр FollowLink учитывается только при использовании символической ссылки. Если нужно проверить только существование символической ссылки на файл, то нужно установить этот параметр в false, а если нужно проверить наличие всего сразу, т.е. и символической ссылки на файл и самого файла, то нужно установить этот параметр в true. Вот примеры использования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, ShellAPI, Classes, Windows; var execInfo: TShellExecuteInfo; begin try //Проверяем, есть ли файл. //Если файла нет, то функция вернёт false. Writeln(TFile.Exists(‘C:Test1test1.txt’)); //Создаём директорию C:Test1. TDirectory.CreateDirectory(‘C:Test1’); //Создаём файл test.txt. TFile.CreateText(‘C:Test1test1.txt’).Free; //Проверяем, есть ли файл ещё раз. //Теперь файл точно есть, поэтому функция вернёт true. Writeln(TFile.Exists(‘C:Test1test1.txt’)); //Теперь проверяем, существует ли символическая ссылка C:Test1TestLink. //Если ссылка не существует, то функция вернёт false. Writeln(TFile.Exists(‘C:Test1TestLink’, false)); //Создадим символическую ссылку C:Test1TestLink на несуществующий файл C:Test1test2.txt. //Для создания символической ссылки нужно обладать правами администратора, //поэтому создавать её будем с помощью утилиты командной строки mklink, //которую будем вызывать от имени администратора. //Чтобы вызвать эту утилиту, придётся сначала создать пакетный файл, //а затем выполнить его. //Итак, сначала создаём пакетный файл с командой mklink. TFile.WriteAllText(‘C:Test1tmp.bat’, ‘mklink C:Test1TestLink C:Test1test2.txt’); //Теперь выполняем пакетный файл от имени администратора. FillChar(execInfo, SizeOf(TShellExecuteInfo), 0); execInfo.cbSize := SizeOf(TShellExecuteInfo); execInfo.lpVerb := PWideChar(‘runas’); execInfo.lpFile := PWideChar(‘C:Test1tmp.bat’); execInfo.fMask := SEE_MASK_NOCLOSEPROCESS; ShellExecuteEx(@execInfo); //Ждём, пока пакетный файл отработает. WaitForSingleObject(execInfo.hProcess, INFINITE); //Проверяем, существует ли символическая ссылка C:Test1TestLink. //Теперь ссылка существует, поэтому функция вернёт true. Writeln(TFile.Exists(‘C:Test1TestLink’, false)); //Проверяем, существует ли символическая ссылка C:Test1TestLink //и файл C:Test1test2.txt, на которую она ссылается. //Ссылка C:Test1TestLink существует, но файл C:Test1test2.txt — нет, //поэтому функция вернёт false. Writeln(TFile.Exists(‘C:Test1TestLink’, true)); //Создаём файл C:Test1test2.txt. TFile.Create(‘C:Test1test2.txt’).Free; //Опять проверяем, существует ли символическая ссылка C:Test1TestLink //и файл C:Test1test2.txt, на которую она ссылается. //Теперь ссылка C:Test1TestLink и файл C:Test1test2.txt существуют, //поэтому функция вернёт true. Writeln(TFile.Exists(‘C:Test1TestLink’, true)); //Удаляем символическую ссылку. TFile.Delete(‘C:Test1TestLink’); //Удаляем директорию C:Test1 и все файлы в ней. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Получение цели из символической ссылки
Чуть выше мы упоминали работу с символической ссылкой, а теперь разберёмся, как узнать, на что ссылается символическая ссылка. Для этого у структуры TFile есть две функции с именем GetSymLinkTarget. Вот пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, ShellAPI, Windows; var execInfo: TShellExecuteInfo; fileName: string; symLinkRec: TSymLinkRec; begin try //Создаём временную директорию. TDirectory.CreateDirectory(‘C:Test1’); //Создаём текстовый файл test.txt. TFile.WriteAllText(‘C:Test1test.txt’, ‘Здравствуй, мир!’); //Создаём символическую ссылку test, которая будет ссылаться на файл test.txt. //Для создания символической ссылки нужно обладать правами администратора, //поэтому создавать её будем с помощью утилиты командной строки mklink, //которую будем вызывать от имени администратора. //Чтобы вызвать эту утилиту, придётся сначала создать пакетный файл, //а затем выполнить его. //Итак, сначала создаём пакетный файл с командой mklink. TFile.WriteAllText(‘C:Test1tmp.bat’, ‘mklink C:Test1test C:Test1test.txt’); //Теперь выполняем пакетный файл от имени администратора. FillChar(execInfo, SizeOf(TShellExecuteInfo), 0); execInfo.cbSize := SizeOf(TShellExecuteInfo); execInfo.lpVerb := PWideChar(‘runas’); execInfo.lpFile := PWideChar(‘C:Test1tmp.bat’); execInfo.fMask := SEE_MASK_NOCLOSEPROCESS; ShellExecuteEx(@execInfo); //Ждём, пока пакетный файл отработает. WaitForSingleObject(execInfo.hProcess, INFINITE); //Получаем имя файла, на который ссылается символическая ссылка. TFile.GetSymLinkTarget(‘C:Test1test’, fileName); //Выводим, полученное имя файла. В результате увидим ‘C:Test1test’. Writeln(fileName); //Получаем все характеристики символической ссылки. TFile.GetSymLinkTarget(‘C:Test1test’, symLinkRec); //Выводим некоторые поля структуры symLinkRec. //Имя целевой папки или файла. Writeln(symLinkRec.TargetName); //Атрибуты целевого файла. Writeln(symLinkRec.Attr); //Размер целевого файла. Writeln(symLinkRec.Size); //Время и дата создания целевого файла или папки. Writeln(FormatDateTime(‘dd.mm.yyyy hh:nn:ss:’, symLinkRec.TimeStamp)); //Удаляем символическую ссылку. TFile.Delete(‘C:Test1test’); //Удаляем директорию C:Test1 и все файлы в ней. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Чтение и изменение характеристик файла
В структуре TFile для чтения и изменения характеристик файла есть следующие методы:
-
- GetAttributes – возвращает атрибуты файла, такие как «только чтение», «системный», «скрытый» и т.п.;
- SetAttributes – устанавливает новый набор атрибутов для файла, такие как «только чтение», «системный», «скрытый» и т.п.;
- GetCreationTime и GetCreationTimeUtc – возвращают дату и время создания файла;
- SetCreationTime и SetCreationTimeUtc – устанавливают дату и время создания файла;
- GetLastAccessTime и GetLastAccessTimeUtc – возвращают дату и время последнего обращения к файлу;
- SetLastAccessTime и SetLastAccessTimeUtc – устанавливают дату и время последнего обращения к файлу;
- GetLastWriteTime и GetLastWriteTimeUtc – возвращают дату и время последней записи в файл.
- SetLastWriteTime и SetLastWriteTimeUtc – устанавливают дату и время последней записи в файл.
Функции, возвращающие и устанавливающие время, в конце имени которых есть суффикс «Utc», возвращают время в формате всемирного координированного времени (UTC).
Кроме приведённых здесь функций есть ещё недокументированные статические функции IntegerToFileAttributes и FileAttributesToInteger. Первая конвертирует атрибуты операционной системы для файла или директории в атрибуты, описанные в перечислении TFileAttribute, а вторая — обратно.
Рассмотрим примеры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Rtti; var fileAttribute: TFileAttribute; begin try //Создаём директорию. TDirectory.CreateDirectory(‘C:Test3’); //Создаём пустые файлы для экспериментов. TFile.Create(‘C:Test3test1.txt’).Free; TFile.Create(‘C:Test3test2.txt’).Free; //Делаем файл скрытым, выставляя ему атрибут faHidden. TFile.SetAttributes(‘C:Test3test1.txt’, [TFileAttribute.faHidden]); //Делаем файл доступным только для чтения, выставляя ему атрибут faReadOnly. TFile.SetAttributes(‘C:Test3test2.txt’, [TFileAttribute.faReadOnly]); //Отображаем все установленные атрибуты файла test1.txt. //В результате отобразятся атрибут faHidden. for fileAttribute in TFile.GetAttributes(‘C:Test3test1.txt’) do Writeln(TRttiEnumerationType.GetName<TFileAttribute>(fileAttribute)); //Отображаем все установленные атрибуты файла test2.txt. //В результате отобразится атрибут faReadOnly. for fileAttribute in TFile.GetAttributes(‘C:Test3test2.txt’) do Writeln(TRttiEnumerationType.GetName<TFileAttribute>(fileAttribute)); //Выводим дату и время создания файла test1.txt. //В результате отобразится дата и время на момент создания файла. Writeln(DateTimeToStr(TFile.GetCreationTime(‘C:Test3test1.txt’))); //Устанавливаем дату и время создания файла. TFile.SetCreationTime(‘C:Test3test1.txt’, StrToDateTime(‘01.02.03 11:12:13’)); //Выводим дату и время создания файла. //В результате отобразится 01.02.2003 11:12:13. Writeln(DateTimeToStr(TFile.GetCreationTime(‘C:Test3test1.txt’))); //Выводим дату и время создания файла в формате UTC, т.е. без учёта часового пояса. //В результате отобразится 01.02.2003 8:12:13, т.е. -3 часа по Москве. Writeln(DateTimeToStr(TFile.GetCreationTimeUtc(‘C:Test3test1.txt’))); //Выводим дату и время последнего обращения к файлу. Writeln(DateTimeToStr(TFile.GetLastAccessTime(‘C:Test3test1.txt’))); //Устанавливаем дату и время последнего обращения к файлу. TFile.SetLastAccessTime(‘C:Test3test1.txt’, StrToDateTime(‘01.02.03 11:12:14’)); //Выводим дату и время последнего обращения к файлу. //В результате отобразится 01.02.2003 11:12:14. Writeln(DateTimeToStr(TFile.GetLastAccessTime(‘C:Test3test1.txt’))); //Выводим дату и время последней записи в файл. Writeln(DateTimeToStr(TFile.GetLastWriteTime(‘C:Test3test1.txt’))); //Устанавливаем дату и время последней записи в файл. TFile.SetLastWriteTime(‘C:Test3test1.txt’, StrToDateTime(‘01.02.03 11:12:15’)); //Выводим дату и время последней записи в файл. //В результате отобразится 01.02.2003 11:12:15. Writeln(DateTimeToStr(TFile.GetLastWriteTime(‘C:Test3test1.txt’))); //Удаляем директорию и все вложенные файлы. TDirectory.Delete(‘C:Test3’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Копирование, перемещение и переименование файлов
Для копирования файла у структуры TFile есть метод Copy, а для перемещения или переименования – метод Move.
Метод Copy кидает ошибку, если файл с таким же именем, как файл назначения существует. Однако вы можете установить третий параметр метода Overwrite в true, и тогда операция копирования всё равно выполнится.
Кроме вышеуказанного случая метод Copy кидает ошибку, если файл, который вы хотите копировать, не существует, неверно указан путь или пользователь не имеет достаточных привилегий для копирования файла в указанное место.
Метод Move кидает ошибку, если файл назначения существует.
Вот пример использования методов Copy и Move:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Создаём директории. TDirectory.CreateDirectory(‘C:Test1’); TDirectory.CreateDirectory(‘C:Test2’); //Создаём файл. TFile.Create(‘C:Test1file1.txt’).Free; //Копируем файл в другую директорию. TFile.Copy(‘C:Test1file1.txt’, ‘C:Test2file1.txt’); //Ещё раз пытаемся скопировать файл, вместо уже существующего. //При этом мы получим ошибку EInOutError с текстом ‘The specified file already exists’. try TFile.Copy(‘C:Test1file1.txt’, ‘C:Test2file1.txt’); except //Отлавливаем ошибку EInOutError и ничего не делаем. on EInOutError do; end; //Опять копируем файл вместо существующего, //но теперь выставляем параметр Overwrite в true. //В этом случае существующий файл заменится на новый. TFile.Copy(‘C:Test1file1.txt’, ‘C:Test2file1.txt’, true); //Переименовываем файл, оставляя его в той же директории. TFile.Move(‘C:Test1file1.txt’, ‘C:Test1file2.txt’); //Переносим файл в другую директорию. TFile.Move(‘C:Test1file2.txt’, ‘C:Test2file2.txt’); //Переносим файл в другую директорию и одновременно переименовываем его. TFile.Move(‘C:Test2file2.txt’, ‘C:Test1file3.txt’); //Удаляем директории и все вложенные файлы. TDirectory.Delete(‘C:Test1’, true); TDirectory.Delete(‘C:Test2’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Шифрование и расшифровывание файлов
Шифрование и расшифровывание файла можно сделать с помощью, соответственно, методов Encrypt и Decrypt. После шифрования вы сможете работать с файлом как и раньше. Всю работу по чтению из зашифрованного файла, запись в него и т.д. берёт на себя операционная система. Вот пример использования функций.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Создаём директорию. TDirectory.CreateDirectory(‘C:Test1’); //Создаём файл. TFile.WriteAllText(‘C:Test1file1.txt’, ‘Содержимое файла’); //Шифруем файл. TFile.Encrypt(‘C:Test1file1.txt’); //Расшифровываем файл. TFile.Decrypt(‘C:Test1file1.txt’); //Удаляем директории и все вложенные файлы. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Замена содержимого файла
У структуры TFile есть метод Replace, позволяющий заменить содержимое файла на содержимое другого файла. По сути, происходит замена одного файла на другой, но при замене сохранятся все атрибуты файла, содержимого которого мы меняем. Атрибуты копируются следующие: время создания, краткое название файла, идентификатор объекта, дискреционный список контроля доступа (DACL), атрибуты безопасности, шифрование, сжатие и именованные потоки (не в заменяемом файле). Однако вы можете сказать функции Replace, что сохранять атрибуты не нужно, в этом случае вы можете установить четвёртый параметр функции IgnoreMetadataErrors в true. Также для заменяемого файла метод Replace создаёт резервную копию.
Вот примеры использования функции Replace:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Создаём директории. TDirectory.CreateDirectory(‘C:Test1’); //Создаём два файла. TFile.WriteAllText(‘C:Test1file1.txt’, ‘Содержимое первого файла’); TFile.WriteAllText(‘C:Test1file2.txt’, ‘Содержимое второго файла’); //Шифруем только второй файл file2.txt. TFile.Encrypt(‘C:Test1file2.txt’); //Заменяем содержимое файла file2.txt на содержимое файла file1.txt. //При этом в файл backup.txt отправляется старое содержимое файла file2.txt. TFile.Replace(‘C:Test1file1.txt’, ‘C:Test1file2.txt’, ‘C:Test1backup.txt’); //После замены содержимого второй файл останется зашифрованным. //В консоли появится надпись ‘Файл file2.txt зашифрован.’. if TFileAttribute.faEncrypted in TFile.GetAttributes(‘C:Test1file2.txt’) then Writeln(‘Файл file2.txt зашифрован.’); //Удаляем директории и все вложенные файлы. TDirectory.Delete(‘C:Test1’, true); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Использование TPath
Структура TPath используется в Delphi для работы с путями. Она содержит только статические методы и свойства. Давайте рассмотрим их.
Специальные символы
При составлении путей к файлам и директориям используются определённые специальные символы, которые могут быть разными в разных операционных системах. Чтобы не ломать голову и не гадать, какие символы для чего нужно использовать, мы можем узнать это у структуры TPath с помощью следующих статических свойств:
-
- DirectorySeparatorChar – символ для разделения директорий разного уровня;
- AltDirectorySeparatorChar – альтернативный символ для разделения директорий разного уровня;
- ExtensionSeparatorChar – символ для разделения имени от расширения в имени файла;
- PathSeparator – символ для разделения путей в переменных окружения;
- VolumeSeparatorChar – символ для отделения буквы диска от остального пути.
Вот пример, с выводом значений всех свойств TPath:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Результат будет ». Writeln(TPath.DirectorySeparatorChar); //Результат будет ‘/’. Writeln(TPath.AltDirectorySeparatorChar); //Результат будет ‘.’. Writeln(TPath.ExtensionSeparatorChar); //Результат будет ‘;’. Writeln(TPath.PathSeparator); //Результат будет ‘:’. Writeln(TPath.VolumeSeparatorChar); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Получение путей к специальным и пользовательским директориям
В каждой современной операционной системе есть директории для хранения документов, видео, музыки и т.п. Кроме того есть разнообразные специальные директории для хранения системных библиотек, временных файлов и т.п. Это сделано для удобства пользователей и разработчиков, а также для поддержания порядка. Для получения путей к таким директориям у структуры TPath есть следующие функции:
-
- GetAlarmsPath – путь к директории для хранения пользовательских звуков для будильника. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS. В Windows и OS X эта функция вернёт то же, что и функция GetMusicPath.
- GetCachePath – путь к директории, в которой ваше приложение может сохранять файлы кеша.
- GetCameraPath – путь к пользовательской директории, в которую сохраняются фотографии, сделанные с помощью камеры устройства. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS. В Windows и OS X эта функция вернёт то же, что и функция GetPicturesPath.
- GetDocumentsPath – путь к директории для хранения документов пользователя.
- GetDownloadsPath – путь к директории для хранения скачанных пользователем файлов. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetHomePath – возвращает либо домашний путь пользователя (в Linux — /home/<username>), либо путь к временной папке приложения (В Windows Vista и позже — C:Users<username>AppDataRoaming), либо путь к хранилищу (в Android — /data/data/<application ID>/files).
- GetLibraryPath – путь к директории для хранения данных вашего приложения. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку. Например, для Windows — C:Program Files<application folder>, для Android — /data/app-lib/<application ID>.
- GetMoviesPath – путь к директории для хранения видео пользователя. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetMusicPath – путь к директории для хранения музыки пользователя. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetPublicPath – путь к директории, где можно сохранять данные приложения, доступные другим приложениям. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS. Например, Windows Vista и позже — C:ProgramData, OS X — /Users/<username>/Public.
- GetPicturesPath – путь к директории для хранения фотографий и картинок пользователя. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetRingtonesPath – путь к директории для хранения пользовательских звуков для звонка. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS. В Windows и OS X эта функция вернёт то же, что и функция GetMusicPath.
- GetSharedAlarmsPath – путь к директории для хранения общих звуков для будильника. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS. В Windows и OS X эта функция вернёт то же, что и функция GetSharedMusicPath.
- GetSharedCameraPath – путь к общей директории, в которую сохраняются фотографии, сделанные с помощью камеры устройства. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS. В Windows и OS X эта функция вернёт то же, что и функция GetSharedPicturesPath.
- GetSharedDocumentsPath – путь к общей директории для хранения документов пользователя. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetSharedDownloadsPath – путь к общей директории для хранения скачанных файлов. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetSharedMoviesPath – путь к общей директории для хранения видео. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetSharedMusicPath – путь к общей папки для хранения музыки. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetSharedPicturesPath – путь к общей директории для хранения фотографий и картинок. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS.
- GetSharedRingtonesPath – путь к общей папке для хранения звуков для звонка. Если операционная система не поддерживает такую директорию, то эта функция вернёт пустую строку, например, iOS. В Windows и OS X эта функция вернёт то же, что и функция GetSharedMusicPath.
- GetTempPath – путь к папке для хранения временных файлов. Файлы, сохранённые здесь, могут быть удалены после перезагрузки системы или между сессиями приложения.
Вот пример использования функций:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Результат будет: C:UsersusernameMusic Writeln(TPath.GetAlarmsPath); //Результат будет: C:UsersusernameAppDataLocal Writeln(TPath.GetCachePath); //Результат будет: C:UsersusernamePictures Writeln(TPath.GetCameraPath); //Результат будет: C:UsersusernameDocuments Writeln(TPath.GetDocumentsPath); //Результат будет: C:UsersusernameAppDataLocal Writeln(TPath.GetDownloadsPath); //Результат будет: C:UsersusernameAppDataRoaming Writeln(TPath.GetHomePath); //Результат будет: C:ProjectsDelphiTokyoIOUtilsTestWin32Debug Writeln(TPath.GetLibraryPath); //Результат будет: C:UsersusernameVideos Writeln(TPath.GetMoviesPath); //Результат будет: C:UsersusernameMusic Writeln(TPath.GetMusicPath); //Результат будет: C:ProgramData Writeln(TPath.GetPublicPath); //Результат будет: C:UsersusernamePictures Writeln(TPath.GetPicturesPath); //Результат будет: C:UsersusernameMusic Writeln(TPath.GetRingtonesPath); //Результат будет: C:UsersPublicMusic Writeln(TPath.GetSharedAlarmsPath); //Результат будет: C:UsersPublicPictures Writeln(TPath.GetSharedCameraPath); //Результат будет: C:UsersPublicDocuments Writeln(TPath.GetSharedDocumentsPath); //Результат будет: C:ProgramData Writeln(TPath.GetSharedDownloadsPath); //Результат будет: C:UsersPublicVideos Writeln(TPath.GetSharedMoviesPath); //Результат будет: C:UsersPublicMusic Writeln(TPath.GetSharedMusicPath); //Результат будет: C:UsersPublicPictures Writeln(TPath.GetSharedPicturesPath); //Результат будет: C:UsersPublicMusic Writeln(TPath.GetSharedRingtonesPath); //Результат будет: c:temp Writeln(TPath.GetTempPath); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Работа с путями
У структуры TPath есть много методов для работы с путями, например, для соединения путей или для разбивки:
-
- ChangeExtension – изменяет расширение файла в указанном пути. Функция не меняет имя реального файла, она только возвращает новую получившуюся строку.
- Combine – соединяет два пути в один. Например, если в первом параметре указан абсолютный путь к директории (например, C:Test1), а во втором – имя файла (например, test.txt), то в результате функция вернёт абсолютный путь к файлу (получим C:Test1test.txt). Если во втором параметре указан абсолютный путь, то функция просто вернёт его, игнорируя первый параметр.
- GetDirectoryName – извлекает путь к директории из пути к файлу. Например, для пути «C:MyAppTemptemp.txt» функция вернёт «C:MyAppTemp». Функция вернёт пустую строку, если в пути к файлу нет части с диском и директорией, например, если вы передадите в функцию строку «test.txt».
- GetExtendedPrefix – возвращает расширенный тип префикса для указанного пути, если он есть. Возможные варианты \? или \?UNC.
- GetExtension – извлекает расширение файла из пути. Результат содержит точку в начале строки. Если у имени файла нет расширения, то функция вернёт пустую строку.
- GetFileName – извлекает имя файла с расширением из пути к файлу. Например, для пути «C:Test1test.txt» функция вернёт «test.txt».
- GetFileNameWithoutExtension – извлекает имя файла без расширения из пути к файлу. Например, для пути «C:Test1test.txt» функция вернёт «test».
- GetFullPath – возвращает абсолютный путь для указанного относительного пути. При создании абсолютного пути функция использует текущую директорию. Например, если текущая директория «C:MyApp», то для пути «Temptest.txt», функция вернёт «C:MyAppTemptest.txt». Если вы передали в функцию абсолютный путь, то функция вернёт его без изменений.
- GetPathRoot – возвращает корень для указанного пути. Например, для пути «C:MyAppTemp», функция вернёт «C:».
- HasExtension – функция проверяет, есть ли у указанного имени файла расширение.
- HasValidFileNameChars – функция проверяет, содержит ли имя файла или шаблон имени файла только разрешённые символы.
- HasValidPathChars – функция проверяет, содержит ли путь только разрешённые символы.
- IsDriveRooted – функция проверяет, является ли путь абсолютным и содержит ли букву логического диска.
- IsExtendedPrefixed – функция проверяет, содержит ли путь расширенный префикс.
- IsPathRooted – проверяет, является ли путь абсолютным.
- IsRelativePath – проверяет, является ли путь относительным.
- IsUNCPath – проверяет, указан ли путь в UNC (Universal Naming Convention) формате. Путь в формате UNC начинается с двух слешей, например, «\computerfolder».
- IsUNCRooted – проверяет, указан ли корень в пути в формате UNC.
- MatchesPattern – проверяет соответствие имени файла шаблону.
Вот примеры использования функций для работы с путями:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils, Rtti; begin try //Меняем расширение файла. Результат будет ‘C:MyAppTemptemp.dat’. Writeln(TPath.ChangeExtension(‘C:MyAppTemptemp.txt’, ‘.dat’)); //Соединяем два пути. Результат будет ‘C:MyAppTemptemp.txt’. Writeln(TPath.Combine(‘C:MyApp’, ‘Temptemp.txt‘)); //Извлекаем путь к директории. Результат будет ‘C:MyAppTemp‘. Writeln(TPath.GetDirectoryName(‘C:MyAppTemptemp.txt‘)); //Получаем тип расширенного префикса. //Результат будет pptNoPrefix. Writeln(TRttiEnumerationType.GetName<TPathPrefixType>( TPath.GetExtendedPrefix(‘C:MyAppTemptemp.txt‘))); //Результат будет pptExtended. Writeln(TRttiEnumerationType.GetName<TPathPrefixType>( TPath.GetExtendedPrefix(‘?C:MyAppTemptemp.txt‘))); //Результат будет pptExtendedUNC. Writeln(TRttiEnumerationType.GetName<TPathPrefixType>( TPath.GetExtendedPrefix(‘?UNCmy_computershared_folder‘))); //Извлекаем расширение файла. Результат будет ‘.txt‘. Writeln(TPath.GetExtension(‘C:MyAppTemptemp.txt‘)); //Извлекаем имя файла с расширением. Результат будет ‘temp.txt‘. Writeln(TPath.GetFileName(‘C:MyAppTemptemp.txt‘)); //Извлекаем имя файла без расширения. Результат будет ‘temp‘. Writeln(TPath.GetFileNameWithoutExtension(‘C:MyAppTemptemp.txt‘)); //Получаем абсолютный путь из относительного. Результат будет ‘C:MyAppTemptemp.txt‘. Writeln(TPath.GetFullPath(‘MyAppTemptemp.txt‘)); //Получаем корень для указанного пути. Результат будет ‘C:‘. Writeln(TPath.GetPathRoot(‘C:MyAppTemptemp.txt’)); //Проверяем, есть ли у имени файла расширение. Результат будет TRUE. Writeln(TPath.HasExtension(‘C:MyAppTemptemp.txt’)); //Проверяем, все ли символы в имени файла разрешены. //Результат будет TRUE. Writeln(TPath.HasValidFileNameChars(‘temp.txt’, false)); //Результат будет FALSE. Writeln(TPath.HasValidFileNameChars(‘t?emp*.txt’, false)); //Проверяем, все ли символы в шаблоне имени файла разрешены. Результат будет TRUE. Writeln(TPath.HasValidFileNameChars(‘t?emp*.txt’, true)); //Проверяем, все ли символы в пути разрешены. //Результат будет TRUE. Writeln(TPath.HasValidPathChars(‘C:MyAppTemptemp.txt’, false)); //Результат будет FALSE. Writeln(TPath.HasValidPathChars(‘C:MyApp<Temp>temp.txt’, false)); //Проверяем, является ли путь абсолютным и начинается ли он с имени диска. //Результат будет TRUE. Writeln(TPath.IsDriveRooted(‘C:MyAppTemptemp.txt’)); //Результат будет FALSE. Writeln(TPath.IsDriveRooted(‘MyAppTemptemp.txt’)); //Результат будет FALSE. Writeln(TPath.IsDriveRooted(‘\my_computerMyAppTemptemp.txt’)); //Проверяем, содержит ли путь расширенный префикс. //Результат будет FALSE. Writeln(TPath.IsExtendedPrefixed(‘C:MyAppTemptemp.txt’)); //Результат будет TRUE. Writeln(TPath.IsExtendedPrefixed(‘\?C:MyAppTemptemp.txt’)); //Проверяем, является ли путь абсолютным. //Результат будет TRUE. Writeln(TPath.IsPathRooted(‘C:MyAppTemptemp.txt’)); //Результат будет TRUE. Writeln(TPath.IsPathRooted(‘MyAppTemptemp.txt’)); //Результат будет FALSE. Writeln(TPath.IsPathRooted(‘..MyAppTemptemp.txt’)); //Результат будет TRUE. Writeln(TPath.IsPathRooted(‘\my_computerMyAppTemptemp.txt’)); //Проверяем, является ли путь относительным. //Результат будет FALSE. Writeln(TPath.IsRelativePath(‘C:MyAppTemptemp.txt’)); //Результат будет FALSE. Writeln(TPath.IsRelativePath(‘MyAppTemptemp.txt’)); //Результат будет TRUE. Writeln(TPath.IsRelativePath(‘..MyAppTemptemp.txt’)); //Результат будет FALSE. Writeln(TPath.IsRelativePath(‘\my_computerMyAppTemptemp.txt’)); //Проверяем, указан ли путь в UNC формате. //Результат будет TRUE. Writeln(TPath.IsUNCPath(‘\?UNCmy_computershared_folder’)); //Проверяем, указан ли корень в пути в формате UNC. //Результат будет TRUE. Writeln(TPath.IsUNCRooted(‘\?UNCmy_computershared_folder’)); //Проверяем соответствие имени файла шаблону. //Результат будет TRUE. Writeln(TPath.MatchesPattern(‘Temp.txt’, ‘*.txt’, false)); //Результат будет TRUE. Writeln(TPath.MatchesPattern(‘Temp.txt’, ‘t???.txt’, false)); //Результат будет FALSE. Writeln(TPath.MatchesPattern(‘Temp.txt’, ‘*.dat’, false)); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Функции для создания временных файлов
Часто в программе нужно создавать временные файлы, для которых нужно придумывать уникальные имена. Чтобы не придумывать свой собственный алгоритм для формирования уникального имени файла вы можете использовать следующие функции структуры TPath: GetTempFileName, GetGUIDFileName или GetRandomFileName. Первая функция создаёт пустой временный файл в папке операционной системы для хранения временных файлов, (см. функцию TPath.GetTempPath) и возвращает абсолютное имя созданного файла. Остальные две функции только генерируют имя файла и не гарантируют его уникальность.
Вот примеры использования функций:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; var fileName: string; begin try //Создаём пустой временный файл в папке для временных файлов операционной системы. fileName := TPath.GetTempFileName; //Имя файла всё время будет разным, например, ‘c:temptmp74D0.tmp’. Writeln(fileName); //Удаляем созданный временный файл. TFile.Delete(fileName); //Создаём GUID, который можно использовать в качестве имени файла. fileName := TPath.GetGUIDFileName(false); //Результат всё время будет разный, например, ‘F45F5BC9F72A454D8DCF205206A0E0C2’. Writeln(fileName); //Чтобы получить из GUID настоящее имя для файла, добавляем путь и расширение. fileName := TPath.Combine(TPath.GetTempPath, fileName + ‘.txt’); //Теперь результат будет ‘c:temp62AD3AB101964F7BBB0216694840F8C4.txt’. Writeln(fileName); //Создаём имя для временного файла. fileName := TPath.GetRandomFileName; //Результат всё время будет разный, например, ‘eJA6Q5HG.0Qs’. Writeln(fileName); //Добавляем путь. fileName := TPath.Combine(TPath.GetTempPath, fileName); //Теперь результат будет ‘c:tempeJA6Q5HG.0Qs’. Writeln(fileName); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Проверка наличия диска
В функции TPath есть функция DriveExists позволяющая определить, есть на компьютере диск, указанный в пути. Вот пример её использования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; begin try //Проверяем, есть ли на компьютере диск, указанный в пути, т.е. диск C:. //Результат будет TRUE. Writeln(TPath.DriveExists(‘C:Temp1’)); //Результат будет TRUE. Writeln(TPath.DriveExists(‘C:’)); //Результат будет FALSE. Writeln(TPath.DriveExists(‘Z:Temp1’)); Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Функции проверки символов для использования в пути и имени файла
Кроме всего вышеперечисленного у структуры TPath есть функции IsValidPathChar и IsValidFileNameChar для проверки символов, подходят ли они для использования в пути и в имени файла и функции GetInvalidFileNameChars и GetInvalidPathChars, которые возвращают символы, которые не допускается использовать в имени файла и в пути. Имея эти символы, вы самостоятельно сможете создать функции, которые будут проверять имена файлов и пути. Посмотрим, что возвращают эти функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, IOUtils; var c: char; begin try //Проверяем, можно ли использовать символы в пути. //Результат будет TRUE. Writeln(TPath.IsValidPathChar(‘a’)); //Результат будет TRUE. Writeln(TPath.IsValidPathChar(»)); //Результат будет FALSE. Writeln(TPath.IsValidPathChar(‘<‘)); //Проверяем, можно ли использовать символы в имени файла. //Результат будет TRUE. Writeln(TPath.IsValidFileNameChar(‘a‘)); //Результат будет FALSE. Writeln(TPath.IsValidFileNameChar(‘‘)); //Результат будет FALSE. Writeln(TPath.IsValidFileNameChar(‘<‘)); //Выдаём символы, которые нельзя использовать в имени файла. //В результате будет примерно так (не все символы могут быть отображены в консоли): //☺☻♥♦♣♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼»*/:<>?| for c in TPath.GetInvalidFileNameChars do Write(c); Writeln; //Выдаём коды символов, которые нельзя использовать в имени файла. //В результате будет: //0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,34,42,47,58,60,62,63,92,124, for c in TPath.GetInvalidFileNameChars do begin Write(integer(c)); Write(‘,’); end; Writeln; //Выдаём символы, которые нельзя использовать в пути. //В результате будет примерно так (не все символы могут быть отображены в консоли): //☺☻♥♦♣♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼»<>| for c in TPath.GetInvalidPathChars do Write(c); Writeln; //Выдаём коды символов, которые нельзя использовать в пути. //В результате будет: //0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,34,60,62,124, for c in TPath.GetInvalidPathChars do begin Write(integer(c)); Write(‘,’); end; Writeln; Readln; except on E: Exception do Writeln(E.ClassName, ‘: ‘, E.Message); end; end. |
Получение атрибутов файлов и директорий
И в завершении вкратце упомяну ещё методы GetAttributes и SetAttributes у структуры TPath. Они умеют читать и устанавливать атрибуты файлов и директорий. Описывать их нет смысла, т.к. они по своим функциям повторяют одноимённые методы структур TFile и TDirectory.
Заключение
Как видите, набор инструментов в юните System.IOUtils очень большой. Поверьте, при работе с файлами он существенно облегчает работу. Надеюсь, что статья понравится не только тем, кто ещё не был знаком с замечательным юнитом System.IOUtils, но и тем, кто уже его использует. Ведь и в том, что, казалось бы, уже хорошо знакомо, часто можно найти что-то новое и неизученное.
Опубликовано 28.12.2020 от evgeniyalf в категории «Delphi